A shell assignment is a single word, with no space after the equal sign. So what you wrote assigns an empty value to thefile
; furthermore, since the assignment is grouped with a command, it makes thefile
an environment variable and the assignment is local to that particular command, i.e. only the call to ls
sees the assigned value.
You want to capture the output of a command, so you need to use command substitution:
thefile=$(ls -t -U | grep -m 1 "Screen Shot")
(Some literature shows an alternate syntax thefile=`ls …`
; the backquote syntax is equivalent to the dollar-parentheses syntax except that quoting inside backquotes is weird sometimes, so just use $(…)
.)
Other remarks about your script:
Combining -t
(sort by time) with -U
(don't sort with GNU ls
) doesn't make sense; just use -t
.
Rather than using grep
to match screenshots, it's clearer to pass a wildcard to ls
and use head
to capture the first file:
thefile=$(ls -td -- *"Screen Shot"* | head -n 1)
It's generally a bad idea to parse the output of ls
. This could fail quite badly if you have file names with nonprintable characters. However, sorting files by date is difficult without ls
, so it's an acceptable solution if you know you won't have unprintable characters or backslashes in file names.
Always use double quotes around variable substitutions, i.e. here write
echo "Most recent screenshot is: $thefile"
Without double quotes, the value of the variable is reexpanded, which will cause trouble if it contains whitespace or other special characters.
You don't need semicolons at the end of a line. They're redundant but harmless.
In a shell script, it's often a good idea to include set -e
. This tells the shell to exit if any command fails (by returning a nonzero status).
If you have GNU find
and sort
(in particular if you're running non-embedded Linux or Cygwin), there's another approach to finding the most recent file: have find
list the files and their dates, and use sort
and read
(here assuming bash
or zsh
for -d ''
to read a NUL-delimited record) to extract the youngest file.
IFS=/ read -rd '' ignored thefile < <(
find -maxdepth 1 -type f -name "*Screen Shot*" -printf "%T@/%p\0" |
sort -rnz)
If you're willing to write this script in zsh instead of bash, there's a much easier way to catch the newest file, because zsh has glob qualifiers that permit wildcard matches not only on names but also on file metadata. The (om[1])
part after the pattern is the glob qualifiers; om
sorts matches by increasing age (i.e. by modification time, newest first) and [1]
extracts the first match only. The whole match needs to be in parentheses because it's technically an array, since globbing returns a list of files, even if the [1]
means that in this particular case the list contains (at most) one file.
#!/bin/zsh
set -e
cd ~/Desktop
thefile=(*"Screen Shot"*(om[1]))
print -r "Most recent screenshot is: $thefile"
In bash there are three options: echo -e
, printf
, and $'...'
.
The last one is the easiest:
$ name="Hello"; name=$name$'\033[34m(Test)\e[0m' ; echo "$name"
Hello(Test)
In this case the color code was stored in the variable. The easiest way to "see" the codes (apart from seeing the color) is to use some hex viewer:
$ echo "$name" | od -vAn -tcx1
H e l l o 033 [ 3 5 m ( T e s t )
48 65 6c 6c 6f 1b 5b 33 35 6d 28 54 65 73 74 29
033 [ 0 m \n
1b 5b 30 6d 0a
Use it when you need to "see" the codes (and why they do or don't work).
The color codes are inside the var, already interpreted. In that way you could create a var for some color, and use it:
$ blue=$'\033[34m'; reset=$'\033[0m'
$ echo "Hello $blue Test $reset Colors"
The other way is to store the codes inside a variable, and interpret them each time their "effect" is needed.
$ blue='\033[34m'; reset='\033[0m'
$ echo "Hello $blue Test $reset Colors"
Hello \033[34m Test \033[0m Colors
$ echo -e "Hello $blue Test $reset Colors"
Hello Test Colors
With "Test" in Blue, and "Colors" in Black (if your console screen is white).
The command echo -e
is not as portable (and safe) as printf:
$ blue='\033[34m'; reset='\033[0m'
$ printf "%s $blue%s $reset%s" "Hello" "Test" "Colors"
Hello Test Colors
The whole list of colors (background) will be visible with (printing an space):
printf '\e[%sm ' {40..47} 0; echo
Or, with foreground colors:
printf '\e[%smColor=%s ' {30..37}{,} 0 0; echo
Best Answer
It's no different with
for
loops than with any other commands, you'd use command substitution:Would store the whole output minus the last newline character added by the last
echo
as a string into the scalar$variable
variable.You'd then do for instance:
Or:
In the
bash
shell, you can also store each line of the output into an element of an array with:To store each space/tab/newline delimited word (assuming the default value of
$IFS
) of the output into an array, you can use the split+glob operator with the glob part disabledWith
zsh
/bash
/ksh93
, you can also do things like:To build the array.
Then in all those, you'd do:
To pass all the elements of the array as arguments to
futher-processing
or:To print each element on one line, piped to
further-processing
Beware however that if the array is empty, that still prints an empty line. You can avoid that by using
print -rC1 --
instead ofprintf '%s\n'
inzsh
or in any Bourne-like shell, define a function such as: