cat output in the case of if else statement
What did you intend with the «if» test? By default, it expects to execute a command and test that return code, which is why it told you it tried to execute a command that started with «Ubuntu. «. Are you interested in whether /etc/issue contains the word «Ubuntu» or are you trying to do something else?
I am interested to know if /etc/issue contains Ubuntu,I would like to assign it to a variable and check
If you want to know whether or not /etc/issue contains Ubuntu, why do you want to assign anything to a variable? Just check the status of grep .
Of course, if the «Ubuntu» in your login banner is coming from an \S in /etc/issue , none of this will pick it up. (-:
2 Answers 2
The if statement runs a command, and checks its exit status. Using $os_version as a command works by expanding it, and running the resulting command line. So if the variable contains Ubuntu 18.04.1 LTS \n \l , it’ll try to to run a command called Ubuntu with the arguments 18.04.1 , LTS , etc.
if [ -n "$os_version" ]; then . fi
to check if the variable is empty or not ( [ -n «$var» ] is true if it’s not empty, while [ -z «$var» ] if the variable is empty).
Alternatively, you could use the grep within the if statement itself as you did in the edit, and set a variable there:
distro=unknown if grep -q Ubuntu < /etc/issue; then distro=ubuntu fi # . later if [ "$distro" = ubuntu ]; then # do something Ubuntu-specific fi
cat a file and check it in one line
You could use the conditional expressions in bash to check the file exists ( -f flag) and not empty with its -s flag.
Also you could just use cp to create a new copy of the file instead of cat -ing it.
if [[ -f /etc/myfile.txt && -s /etc/myfile.txt ]]; then cp /etc/myfile.txt newfile.txt fi
Not sure why you would bother using a one-line instead of proper readable example. Anyway you can use the above logic as
[[ -f /etc/myfile.txt && -s /etc/myfile.txt ]] && cp /etc/myfile.txt newfile.txt
I am not sure why my comments were deleted but I still think -r might be useful. Or did you find something that would say otherwise?
Keeping it a little readable I would write something like test -f /etc/myfile.txt && test -s /etc/myfile.txt && cp /etc/myfile.txt newfile.txt when a oneliner is needed.
[ -s "/etc/myfile.txt" ] && cat /etc/myfile.txt > newfile.txt
You don't have to cat the file, use cp instead.
Here, -s option checks if file is non-zero in size. From man test on a Linux box,
-s FILE FILE exists and has a size greater than zero
EDIT: Above solution is enough if /etc/myfile.txt is a regular file. If one wants to check if it is a regular file before checking the non-zero size, -f flag is needed
[[ -f "/etc/myfile.txt" && -s "/etc/myfile.txt" ]] && cp /etc/myfile.txt newfile.txt
Checking if string exists in file with cat | grep
The output always presents "line exists, skipping" in bot cases where the line exists and does not. Adding the output into a variable e.g. x, substituting the line in the if statement with the variable produces the correct output, How may I 'skip' the variable route and run the command directly?
if [ -z `sudo cat /etc/sudoers | grep "$USER ALL=(ALL) NOPASSWD:ALL"` ]; then echo "no out"; else echo "out"; fi works fine.
my ownly concern is the output if "bash: [: too many arguments", I receive my answer but why does the error occur?
if [[ -z `sudo cat /etc/sudoers | grep "$USER ALL=(ALL) NOPASSWD:ALL"` ]]; then echo "no out"; else echo "out"; fi should be fine.
1 Answer 1
grep can be used as a condition command. It returns true when the pattern matches. Here, you want a fixed-string search ( -F ) and probably to match on the full line ( -x ):
if sudo cat /etc/sudoers | grep -xqFe "$USER ALL=(ALL) NOPASSWD:ALL" then echo found else echo not found fi
Or if the sudoers configuration allows you to run the grep command in addition to the cat one:
if sudo grep -xqFe "$USER ALL=(ALL) NOPASSWD:ALL" /etc/sudoers then echo found else echo not found fi
-q tells grep to be quiet, to just return the true/false status via the exit status but not output anything on stdout.
Note the sudoers configuration can include other configuration files which that approach will not take into account. Same for permissions granted to groups that the user is member of.
You may want to use sudo -lU "$USER" instead.
Conditional pipeline
Under certain conditions I would like to add a cmd3 between cmd2 and cmd4 . Is there a way to create a kind of conditional pipeline without saving the result of cmd2 into a temporary file? I would think of something like:
cmd1 < input.txt |\ cmd2 |\ ($? cmd3 : cat ) |\ cmd4 |\ cmd5 |\ cmd6 |\ (. ) |\ cmdN > result.txt
7 Answers 7
Just the usual && and || operators:
Although, as specified by this answer, you would generally prefer if . else , if you're after the if-else syntax:
. cmd2 | if [ -n "$DEFINED" ]; then cmd3; else cat; fi | cmd4 | .
(Note that no trailing backslash is needed when the line ends with pipe.)
Update according to Jonas' observation.
If cmd3 may terminate with non-zero exit code and you not want cat to process the remaining input, reverse the logic:
Reversing the logic doesn't help. If cat returns with a non-zero exit status (common when getting a SIGPIPE), then cmd3 will be run. Better use if/then/else as in Thor's answer or other solutions that avoid running cat .
if / else / fi works. Assuming any Bourne-like shell:
All the answers given so far replace cmd3 with cat . You can also avoid running any command with:
if [ -n "$DEFINE" ]; then alias maybe_cmd3='cmd3 |' else alias maybe_cmd3='' fi cmd1 | cmd2 | maybe_cmd3 cmd4 | . | cmdN > result.txt
That's POSIX, but note that if in a bash script where bash is not in sh -mode (like with a script starting with #! /path/to/bash - ), you'll need to enable alias expansion with shopt -s expand_aliases (or set -o posix ).
Another approach that still doesn't run any unnecessary command is to use eval:
if [ -n "$DEFINE" ]; then maybe_cmd3='cmd3 |' else maybe_cmd3='' fi eval " cmd1 | cmd2 | $maybe_cmd3 cmd4 | . | cmdN > result.txt"
eval " cmd1 | cmd2 | $ cmd4 | . | cmdN > result.txt"
On Linux (at least), instead of cat , you could use pv -q which uses splice() instead of read() + write() to pass the data across between the two pipes which avoids having the data moved twice between kernel and user space.
Another option could be to define a function that runs a give command piped to or from cmd3 or not depending on $DEFINE .
if [ -n "$DEFINE" ]; then maybe_preprocess_with_cmd3() < cmd3 | "$@"; >maybe_postprocess_with_cmd3() < "$@" | cmd3; >else maybe_preprocess_with_cmd3() < "$@"; >maybe_postprocess_with_cmd3() < "$@"; >fi
cmd1 | cmd2 | maybe_preprocess_with_cmd3 cmd4 | . | cmdN > result.txt
cmd1 | maybe_postprocess_with_cmd3 cmd2 | cmd4 | . | cmdN > result.txt