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 your script, these assignments
normal='\e[0m'
yellow='\e[33m'
put those characters literally into the variables, i.e., \e[0m, rather than the escape sequence. You can construct an escape character using printf
(or some versions of echo
), e.g.,
normal=$(printf '\033[0m')
yellow=$(printf '\033[33m')
but you would do much better to use tput
, as this will work for any correctly set up terminal:
normal=$(tput sgr0)
yellow=$(tput setaf 3)
Looking at your example, it seems that the version of printf
you are using treats \e
as the escape character (which may work on your system, but is not generally portable to other systems). To see this, try
yellow='\e[33m'
printf 'Yellow:%s\n' $yellow
and you would see the literal characters:
Yellow:\e[33m
rather than the escape sequence. Putting those in the printf
format tells printf
to interpret them (if it can).
Further reading:
Best Answer
Parameter expansion isn’t the same as arithmetic evaluation.
In an arithmetic expression, a text string such as
a
is interpreted as a variable name, and the value of that variable is processed as an arithmetic expression itself, and the result used in the containing arithmetic expression. Thuscauses the value of the variable
a
,1+1
, to be evaluated as an arithmetic expression, yielding2
, and the whole expression becomes$((2 * 3))
, i.e.6
.In the same context,
$a
is replaced with the value ofa
as-is, in an earlier phase (this is parameter expansion); sobecomes
$((1+1 * 3))
, which is4
following the usual precedence rules.In the second example,
$((++a))
is processed before$b
(processing takes place from left to right). The arithmetic expression causesa
to be evaluated as an arithmetic expression itself, as above; the value ofa
isa=b++
, which when evaluated, assigns the value ofb
(itself evaluated as an arithmetic expression) toa
, then incrementsb
. Thus after evaluatinga
,b
is2
anda
is1
. Evaluating$((++a))
incrementsa
and returns its value, which is now2
.$b
is just replaced with the value ofb
,2
as well. After the quoted argument toecho
is processed, theecho
is run with2 2
as its sole argument, producing the output you saw.