find -exec with 2 commands doesn't work (error incomplete staement)

Hi Gurues,

I need to modify an existing script that uses find to search a folder, and then move its contents to a folder. What I need to do is run gzip on each file after it's moved.

So, I ran this little test:

Put a ls.tar file on my $HOME, mkdir tmp, and then:

virtuo@tnpmprd01: find . -name '*.tar' -exec \(mv {} tmp; cd tmp; gzip {}\) \;
find: incomplete statement
gzip: {}): No such file or directory
gzip: ;: No such file or directory
virtuo@tnpmprd01:

with parenthesis off, same error:

virtuo@tnpmprd01: find . -name '*.tar' -exec mv {} tmp; cd tmp; gzip {} \;
find: incomplete statement
ksh: tmp:  not found
gzip: {}: No such file or directory
gzip: ;: No such file or directory
virtuo@tnpmprd01:

Any clue?

Thanks a lot,

Leo

The first semicolon after -exec is not escaped, so the shell interprets it as the end of the find command. find then complains because its -exec is not properly terminated. Then the cd tmp fails because tmp does not exit. Then gzip is annoyed because it can't find any files named {} and ;.

However, prematurely terminated find command aside, you'd still have problems; find's -exec primary does not execute a shell unless you ask it too. For example, the following will not work:

find . -name '*.tar' -exec 'cmd1 ' {} '; cmd2 ' {} '; cmd3 ' {} \;

At best, that would call cmd1 with everything that follows it as an argument.

For what you're trying to do, you need to ask -exec to execute a shell to process your sequence of commands (which is a small shell script):

find . -name '*.tar' -exec sh -c 'cmd1 "$1"; cmd2 "$1"; cmd3 "$1"' sh {} \;

Regards and welcome to the forum,
Alister

2 Likes

Thanks Alister,

From the command line it works ok. However, from inside my sh script, it doesn't, no matter how do I scape the quotes, doubles, singles, etc.

Currently I have in the script:

${find} $_dir -type f -exec sh -c \'${mv} \"\$1\" ${_archive_dir}; cd ${_archive_dir}; ${gzip} \"\$1\"\' sh {} \;

and the scripts is doing:

+ /usr/bin/find /appl/virtuo/var/loader/spool/ericssonumtsmgw_r51/R51/bad -type f -exec sh -c '/usr/bin/mv "$1" /appl/virtuo/var/loader/archive/20110923093606/ericssonumtsmgw_r51-bad
/usr/bin/find: incomplete statement
+ cd /appl/virtuo/var/loader/archive/20110923093606/ericssonumtsmgw_r51-bad
+ /usr/bin/gzip "$1"' sh {} ;
gzip: "$1"': No such file or directory
gzip: sh: No such file or directory
gzip: {}: No such file or directory
gzip: ;: No such file or directory

As You see, only the first command of the SH is being executed...

You know what could be wrong?

Thanks again,

Leo

---------- Post updated at 09:38 AM ---------- Previous update was at 09:36 AM ----------

Forgot to add that the directory is empty... the mv didn't worked, the cd did, and the gzip is not translating "$1" to the file name...

Instead, try:

${find} $_dir -type f -exec sh -c '"'"${mv}"'" "$1" "'"${_archive_dir}"'"; cd "'"${_archive_dir}"'"; "'"${gzip}"'" "$1"' sh {} \;

Alternatively, you can pass all the variables as arguments to the invoked sh:

${find} $_dir -type f -exec sh -c '"$1" "$4" "$2"; cd "$2"; "$3" "$4"' sh "${mv}" "${_archive_dir}" "${gzip}" {} \;

However, given that you're trying to use variables defined in the current sh execution environment, it's probably easiest (and more efficient than spawning a shell per file found) to just pipe pathnames from find into a while read loop.

${find} $_dir -type f | while read fn; do
    ${mv} "$fn" ${_archive_dir}
    cd ${_archive_dir}
    ${gzip} "$fn"
    cd -
done

It's always best to provide a full-context in the original problem statement. It often saves everyone involved from wasted time and effort. If none of those suggestions work (be very careful with the quoting), then provide your entire script so that we can have a proper look.

Regards,
Alister