12

I am writing a script which loops into recent files in a folder and executes a command...

#!/bin/bash
cd /home/Downloads
recent_files = ($(ls -t | head -20))
for file in "${recent_files[@]}"
do
    ./cmd $file
done

I get the following syntax error:

line 3: syntax error near unexpected token `('
line 3: `recent_files = ($(\ls -t | head -20))'

5 Answers5

24

Bash variable assignment scheme is var=value i.e. extra spaces are not allowed.

Removing them will correctly assign ls -t | head -20 output as an array.

So your script should be :

#!/bin/bash
cd /home/Downloads
recent_files=($(ls -t | head -20))
for file in "${recent_files[@]}"
do
    ./cmd $file
done
9

You don't need to store the output in a variable at all:

cd /home/Downloads
command ls -t | head -20 | while read -r file; do
    ./cmd "$file"
done

or even

command ls -t | head -20 | xargs -L 1 ./cmd

A safer way to work this is with stat. It still doesn't protect you from filenames with newlines, but

stat -c "%Y %n" * | sort -rn | head -20 | cut -d" " -f2- | xargs -L 1 ./cmd

To be foolproof, find -- probably need GNU tools for the null-byte handling

find . "${find_options[@]}" -printf '%T@ %p\0' | 
sort -zrn |
xargs -0 -L 1 sh -c './cmd "$(echo "$0" | sed "1s/^[0-9.]\\+ //")"'

the find_options array can be used as a placeholder to add filtering directives, such as

find_options=( -type f )
find_options+=( -maxdepth 1 )
find_options+=( -name '*.txt' )

and so on

glenn jackman
  • 85,964
  • Since it is generally recommended that the output of ls should not be used in scripts, I tried to figure out how to do this with find. I got as far as find . -maxdepth 1 -type f -printf '%c\t%f\n' -name '*', but the date is still hard to sort. It's not in epoch format yet. The tab is for use later with cut after sorting. AFAIK, the answer above (and all the others based on using ls) will potentially return directory names as well as file names - which might cause unexpected results with a command which expects to see a regular file. – Joe May 09 '15 at 02:38
5

You have to remove the spaces around the equal sign.

recent_files=($(ls -t | head -20))

Then it should work.

karel
  • 2,030
  • Removing the outer parentheses would cause recent_files to be a scalar rather than an array, which the ${recent_files[@]} syntax on the next line indicates is not what the OP wants. – jwodder May 03 '15 at 03:15
3

I think there's a simpler answer...

#!/bin/bash
cd /home/Downloads
for file in $(ls -t | head -20)
do
    ./cmd $file
done
1

The error message says it all. However, as a help to such problems in the future, you can adopt some practices:

  1. Comment out the line that is throwing up an error like so:
recent_files=($(ls -t | head -20))

and run

bash -n myScript.sh

This will check errors in the script.

  1. Comment out a block of lines like so:

    : << 'EOF'
    lines
    of
    code
    EOF
    

and run

bash -n myScript.sh

Using this methodology, you will be able to zoom in on the erroneous line in your script.

Coming back to your specific problem, many have already answered what is causing it - the spaces around the = sign.

sourav c.
  • 105