Grep in Ubuntu 14.04 - linux

I just recently updated my Ubuntu VM to 14.04 and before this I could run the grep command
grep -nr "piece of text"
or any other grep command for that matter and it would finish in 2-10 seconds in the directory I was working on. Now for some reason (no idea if this was caused by the update) running any grep command in the same dir I was working in just seems to hang there (idk if it does complete because I don't want to wait over a min or so for a search to happen) instead of showing my results. Any idea of what's happening or what I can do and try to fix it?

You aren't specifying the files to grep, so it's trying to read STDIN. I think you wanted
grep -nr "piece of text" *
Note the asterisk is globbed to all files in the current path.

which grep are you using ? -r option default to '.' directory in gnu case
which grep :
/bin/grep --version
/bin/grep (GNU grep) 2.18)
...
this is gnu grep :
echo "truc" >/tmp/truc
cd /tmp; grep -nr "truc";
using gnu grep -> it search recursively files in . of current directory ( here in /tmp ) and displays them containing "truc"
if /bin/grep --version is busybox :
BusyBox v1.22.1 (Debian 1:1.22.0-8) multi-call binary.
Usage:
grep [-HhnlLoqvsriwFEz] [-m N] [-A/B/C N] PATTERN/-e PATTERN.../-f FILE [FILE]...
using busybox : cd /tmp; busybox grep -nr "truc";
wait indefinitely that you type content of file on STDIN ( stop with Ctrl+C ).
it reads STDIN as a file ( not even caring that -n indicates a directory recursion ).

As others answered, and as POSIX grep or GNU grep documentation tells, you need to specify some file to the standard grep command. (But GNU grep defaults to current directory . if -r is given without a directory name; however if -r is not given at all, grep defaults to read - i.e. the stdin).
BTW, I would also recommend the ack utility, packaged as the ack-grep package on Ubuntu or Debian. It does not need any files (default is the source code files under current directory) and it avoids useless non-source files (version control or object files...):
ack "piece of text"
Also, under emacs, you can use M-x grep

Related

Linux commands to get Latest file depending on file name

I am new to linux. I have a folder with many files in it and i need to get the latest file depending on the file name. Example: I have 3 files RAT_20190111.txt RAT_20190212.txt RAT_20190321.txt . I need a linux command to move the latest file here RAT20190321.txt to a specific directory.
If file pattern remains the same then you can try below command :
mv $(ls RAT*|sort -r|head -1) /path/to/directory/
As pointed out by #wwn, there is no need to use sort, Since the files are lexicographically sortable ls should do the job already of sorting them so the command will become :
mv $(ls RAT*|tail -1) /path/to/directory
The following command works.
ls | grep -v '/$' |sort | tail -n 1 | xargs -d '\n' -r mv -- /path/to/directory
The command first splits output of ls with newline. Then sorts it, takes the last file and then it moves this to the required directory.
Hope it helps.
Use the below command
cp ls |tail -n 1 /data...

sed can't change a file when called in postinstall [duplicate]

Is there an invocation of sed todo in-place editing without backups that works both on Linux and Mac? While the BSD sed shipped with OS X seems to need sed -i '' …, the GNU sed Linux distributions usually come with interprets the quotes as empty input file name (instead of the backup extension), and needs sed -i … instead.
Is there any command line syntax which works with both flavors, so I can use the same script on both systems?
If you really want to just use sed -i the 'easy' way, the following DOES work on both GNU and BSD/Mac sed:
sed -i.bak 's/foo/bar/' filename
Note the lack of space and the dot.
Proof:
# GNU sed
% sed --version | head -1
GNU sed version 4.2.1
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file file.bak
% cat ./file
bar
# BSD sed
% sed --version 2>&1 | head -1
sed: illegal option -- -
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file file.bak
% cat ./file
bar
Obviously you could then just delete the .bak files.
This works with GNU sed, but not on OS X:
sed -i -e 's/foo/bar/' target.file
sed -i'' -e 's/foo/bar/' target.file
This works on OS X, but not with GNU sed:
sed -i '' -e 's/foo/bar/' target.file
On OS X you
can't use sed -i -e since the extension of the backup file would be set to -e
can't use sed -i'' -e for the same reasons—it needs a space between -i and ''.
When on OSX, I always install GNU sed version via Homebrew, to avoid problems in scripts, because most scripts were written for GNU sed versions.
brew install gnu-sed --with-default-names
Then your BSD sed will be replaced by GNU sed.
Alternatively, you can install without default-names, but then:
Change your PATH as instructed after installing gnu-sed
Do check in your scripts to chose between gsed or sed depending on your system
As Noufal Ibrahim asks, why can't you use Perl? Any Mac will have Perl, and there are very few Linux or BSD distributions that don't include some version of Perl in the base system. One of the only environments that might actually lack Perl would be BusyBox (which works like GNU/Linux for -i, except that no backup extension can be specified).
As ismail recommends,
Since perl is available everywhere I just do perl -pi -e s,foo,bar,g target.file
and this seems like a better solution in almost any case than scripts, aliases, or other workarounds to deal with the fundamental incompatibility of sed -i between GNU/Linux and BSD/Mac.
Answer: No.
The originally accepted answer actually doesn't do what is requested (as noted in the comments). (I found this answer when looking for the reason a file-e was appearing "randomly" in my directories.)
There is apparently no way of getting sed -i to work consistently on both MacOS and Linuces.
My recommendation, for what it is worth, is not to update-in-place with sed (which has complex failure modes), but to generate new files and rename them afterwards. In other words: avoid -i.
There is no way to have it working.
One way is to use a temporary file like:
TMP_FILE=`mktemp /tmp/config.XXXXXXXXXX`
sed -e "s/abc/def/" some/file > $TMP_FILE
mv $TMP_FILE some/file
This works on both
Here's another version that works on Linux and macOS without using eval and without having to delete backup files. It uses Bash arrays for storing the sed parameters, which is cleaner than using eval:
# Default case for Linux sed, just use "-i"
sedi=(-i)
case "$(uname)" in
# For macOS, use two parameters
Darwin*) sedi=(-i "")
esac
# Expand the parameters in the actual call to "sed"
sed "${sedi[#]}" -e 's/foo/bar/' target.file
This does not create a backup file, neither a file with appended quotes.
The -i option is not part of POSIX Sed. A more portable method would be
to use Vim in Ex mode:
ex -sc '%s/alfa/bravo/|x' file
% select all lines
s replace
x save and close
Steve Powell's answer is quite correct, consulting the MAN page for sed on OSX and Linux (Ubuntu 12.04) highlights the in-compatibility within 'in-place' sed usage across the two operating systems.
JFYI, there should be no space between the -i and any quotes (which denote an empty file extension) using the Linux version of sed, thus
sed Linux Man Page
#Linux
sed -i""
and
sed OSX Man page
#OSX (notice the space after the '-i' argument)
sed -i ""
I got round this in a script by using an alias'd command and the OS-name output of 'uname' within a bash 'if'. Trying to store OS-dependant command strings in variables was hit and miss when interpreting the quotes. The use of 'shopt -s expand_aliases' is necessary in order to expand/use the aliases defined within your script. shopt's usage is dealt with here.
Portable script for both GNU systems and OSX:
if [[ $(uname) == "Darwin" ]]; then
SP=" " # Needed for portability with sed
fi
sed -i${SP}'' -e "s/foo/bar/g" -e "s/ping/pong/g" foobar.txt
I ran into this problem. The only quick solution was to replace the sed in mac to the gnu version:
brew install gnu-sed
If you need to do sed in-place in a bash script, and you do NOT want the in-place to result with .bkp files, and you have a way to detect the os (say, using ostype.sh), -- then the following hack with the bash shell built-in eval should work:
OSTYPE="$(bash ostype.sh)"
cat > myfile.txt <<"EOF"
1111
2222
EOF
if [ "$OSTYPE" == "osx" ]; then
ISED='-i ""'
else # $OSTYPE == linux64
ISED='-i""'
fi
eval sed $ISED 's/2222/bbbb/g' myfile.txt
ls
# GNU and OSX: still only myfile.txt there
cat myfile.txt
# GNU and OSX: both print:
# 1111
# bbbb
# NOTE:
# if you just use `sed $ISED 's/2222/bbbb/g' myfile.txt` without `eval`,
# then you will get a backup file with quotations in the file name,
# - that is, `myfile.txt""`
The problem is that sed is a stream editor, therefore in-place editing is a non-POSIX extension and everybody may implement it differently. That means for in-place editing you should use ed for best portability. E.g.
ed -s foobar.txt <<<$',s/foo/bar/g\nw'
Also see https://wiki.bash-hackers.org/howto/edit-ed.
You can use sponge. Sponge is an old unix program, found in moreutils package (both in ubuntu and probably debian, and in homebrew in mac).
It will buffer all the content from the pipe, wait until the pipe is close (probably meaning that the input file is already close) and then overwrite:
From the man page:
Synopsis
sed '...' file | grep '...' | sponge file
The following works for me on Linux and OS X:
sed -i' ' <expr> <file>
e.g. for a file f containing aaabbaaba
sed -i' ' 's/b/c/g' f
yields aaaccaaca on both Linux and Mac. Note there is a quoted string containing a space, with no space between the -i and the string. Single or double quotes both work.
On Linux I am using bash version 4.3.11 under Ubuntu 14.04.4 and on the Mac version 3.2.57 under OS X 10.11.4 El Capitan (Darwin 15.4.0).

Using alias to shorten my command?

I am used to searching specific keywords under Linux.
For example, I may search "TAIWAN" under home by
grep -i -r TAIWAN ./ | grep -v".svn"
However, I thought this is a little redundant; I want to use an alias so I can type
grep TAIWAN
and then the alias will expand my command into
grep -i -r TAIWAN ./ | grep -v".svn"
How could I achieve this?
You won't be able to do it with an alias, but you can create a bash function:
mygrep () { grep -i -r $* ./ | grep -v".svn"; }
I don't believe alias can accomplish what you want because there is no way to reorder the arguments. It is simpler to make a small shell command. Rather than replace grep, and thus possibly mess up programs which expect grep to behave in a certain way, I'd give it a new name such as rgrep.
#!/bin/sh
grep -i -r "$#" | grep -v .svn
Put that somewhere in your PATH such as ~/bin and make it executable with chmod +x ~/bin/rgrep. Then you can rgrep TAIWAN ..
Unfortunately, this will ignore lines which contain .svn as well as files.
You could try to fix up the grep -v pattern match to only match the file part of the grep output, or you could whip up a more complicated command using find instead of grep -r... or you can use ack.
Ack is a better grep and will avoid version control directories and other common files and directories you don't care about. It will also automatically use a pager and color the output.

xargs can't get user input?

i have a sample code like this:
CMD="svn up blablabla | grep -v .tgz"
echo $CMD | xargs -n -P ${PARALLEL:=20} -- bash -c
the purpose is to run svn update in parallel. However when encounter the conflicts, which should prompt out several selection for users to choose, it just passes without waiting for user input. And an error is shown:
Conflict discovered in 'blablabla'.
Select: (p) postpone, (df) diff-full, (e) edit,
(mc) mine-conflict, (tc) theirs-conflict,
(s) show all options: svn: Can't read stdin: End of file found
Is there any way to fix this?
Thanks
Yes, there is a way to fix this! See the answer to how to prompt a user from a script run with xargs. Long story short, use
xargs -a FILENAME your_script
or
xargs -a <(cat FILENAME) your_script
The first version actually reads lines from a file, and the second one fakes reading lines from a file, which is convenient for using xargs in pipe chains with awk or perl. Remember to use the -0 flag if you don't want to break on whitespace!
Another solution, which doesn't rely on Bash but on GNU's flavor of xargs, is to use the -o or --open-tty option:
echo $CMD | xargs -n -P ${PARALLEL:=20} --open-tty -- bash -c
From the manpage:
-o, --open-tty
Reopen stdin as /dev/tty in the child process before executing the command. This is use‐
ful if you want xargs to run an interactive application.

Identify the files opened a particular process on linux

I need a script to identify the files opened a particular process on linux
To identify fd :
>cd /proc/<PID>/fd; ls |wc –l
I expect to see a list of numbers which is the list of files descriptors' number using in the process. Please show me how to see all the files using in that process.
Thanks.
The command you probably want to use is lsof. This is a better idea than digging in /proc, since the command is a more clear and a more stable way to get system information.
lsof -p pid
However, if you're interested in /proc stuff, you may notice that files /proc/<pid>/fd/x is a symlink to the file it's associated with. You can read the symlink value with readlink command. For example, this shows the terminal stdin is bound to:
$ readlink /proc/self/fd/0
/dev/pts/43
or, to get all files for some process,
ls /proc/<pid>/fd/* | xargs -L 1 readlink
While lsof is nice you can just do:
ls -l /proc/pidoftheproces/fd
lsof -p <pid number here> | wc -l
if you don't have lsof, you can do roughly the same using just /proc
eg
$ pid=1825
$ ls -1 /proc/$pid/fd/*
$ awk '!/\[/&&$6{_[$6]++}END{for(i in _)print i}' /proc/$pid/maps
You need lsof. To get the PID of the application which opened foo.txt:
lsof | grep foo.txt | awk -F\ '{print $2}'
or what Macmede said to do the opposite (list files opened by a process).
lsof | grep processName

Resources