The command is not evaluated and the string is stored as is in the variable. It's the globbing patterns that are expanded when you output the unquoted variable.
cmd="ls -lrt /client/*/ver* /client/*/*/ver* | grep 299 | tail -1"
This is safe and would set cmd
to the literal string ls -lrt /client/*/ver* /client/*/*/ver* | grep 299 | tail -1
. The difference between using double quotes and single quotes around the string doesn't matter in this case (but I would have used single quotes as this is a static string). It doesn't matter in the sense that there is no expansion of anything for the shell to do in the string. Had there been variables or command substitutions in the string, the shell would have expanded those, but there are none.
When you output the value of the variable using echo $cmd
, the globbing patterns would be expanded (this is what happens), but the command would still not be run.
To stop the globbing patterns from being expanded, double quote the variable expansion as "$cmd"
. Personally, I would use
printf '%s\n' "$cmd"
to print its value. See "Why is printf better than echo?" for why.
Related:
Note also that since you're parsing the output of ls -l
(which in itself is not safe), and then grepping for a number, you would pick that number up from anywhere in the ls -l
output, for example in the size of a file.
I would advise that you rethink what the bigger picture issue is that you're trying to solve, and then that you ask a brand new question about that instead.
To find the most recently modified regular file out of a list of pathnames, you may do
unset newest
for pathname in /client/*/ver* /client/*/*/ver*; do
if [ -f "$pathname" ] && [ "$pathname" -nt "$newest" ]; then
newest=$pathname
fi
done
This is assuming that the filename globbing patterns that you've mentioned expands to the names that you'd like to check. To additionally restrict this to only allow filenames containing the string 299
:
unset newest
for pathname in /client/*/ver* /client/*/*/ver*; do
if [[ "${pathname##*/}" == *299* ]] &&
[ -f "$pathname" ] && [ "$pathname" -nt "$newest" ]; then
newest=$pathname
fi
done
If the globbing patterns expand to directories that you need to look into recursively, then using bash
:
shopt -s globstar
unset newest
for pathname in /client/*/ver*/** /client/*/*/ver*/**; do
if [[ "${pathname##*/}" == *299* ]] &&
[ -f "$pathname" ] && [ "$pathname" -nt "$newest" ]; then
newest=$pathname
fi
done
Example of running this over SSH:
ssh user@host bash -s -O globstar <<'END_SCRIPT'
for pathname in /client/*/ver*/** /client/*/*/ver*/**; do
if [[ "${pathname##*/}" == *299* ]] &&
[ -f "$pathname" ] && [ "$pathname" -nt "$newest" ]; then
newest=$pathname
fi
done
printf 'Newest file: %s\n' "$newest"
END_SCRIPT
#!
line. See https://unix.stackexchange.com/questions/444946 – Kusalananda Jul 11 '18 at 06:47bash
. The string is stored and outputted as is. The only thing is that you're echoing the expanded variable unquoted, which would invoke filename globbing. Usingprintf '%s\n' "$cmd"
would prevent that (or evenecho "$cmd"
). But the command would never be evaluated (executed). – Kusalananda Jul 11 '18 at 06:56tail -1
incmd
? – Kusalananda Jul 11 '18 at 07:12bash
shell just does not do that. – Kusalananda Jul 11 '18 at 08:50|
,grep
, etc tols
. Please re-test, is this what you tried last time. (can not reproduce) – ctrl-alt-delor Jul 11 '18 at 12:39cmd
variable being unquoted withecho
. See my updated answer. – Kusalananda Jul 13 '18 at 12:40