简体   繁体   中英

Circumvent Argument list too long in script (for loop)

I've seen a few answers regarding this, but as a newbie, I don't really understand how to implement that in my script.

it should be pretty easy (for those who can stuff like this)

I'm using a simple

for f in "/drive1/"images*.{jpg,png}; do 

but this is simply overloading and giving me

Argument list too long

How is this easiest solved?

Argument list too long workaroud

Argument list length is something limited by your config.

getconf ARG_MAX
2097152

But after discuss around differences between specifics and system (os) limitations (see comments from that other guy ), this question seem wrong:

Regarding discuss on comments, OP tried something like:

ls "/simple path"/image*.{jpg,png} | wc -l
bash: /bin/ls: Argument list too long

This happen because of OS limitation, not !!

But tested with OP code, this work finely

for file in ./"simple path"/image*.{jpg,png} ;do echo -n a;done | wc -c
70980

Like:

 printf "%c" ./"simple path"/image*.{jpg,png} | wc -c

Reduce line length by reducing fixed part :

First step: you could reduce argument length by:

cd "/drive1/"
ls images*.{jpg,png} | wc -l

But when number of file will grow, you'll be buggy again...

More general workaround:

find "/drive1/" -type f \( -name '*.jpg' -o -name '*.png' \) -exec myscript {} +

If you want this to NOT be recursive, you may add -maxdepth as 1st option:

find "/drive1/" -maxdepth 1 -type f \( -name '*.jpg' -o -name '*.png' \) \
    -exec myscript {} +

There, myscript will by run with filenames as arguments. The command line for myscript is built up until it reaches a system-defined limit.

myscript /drive1/file1.jpg '/drive1/File Name2.png' /drive1/...

From man find :

 -exec command {} + This variant of the -exec action runs the specified command on the selected files, but the command line is built by appending each selected file name at the end; the total number of invoca‐ tions of the command will be much less than the number of matched files. The command line is built in much the same way that xargs builds its command lines. Only one instance of `{}'

Inscript sample

You could create your script like

#!/bin/bash

target=( "/drive1" "/Drive 2/Pictures" )

[ "$1" = "--run" ] && exec find "${target[@]}" -type f \( -name '*.jpg' -o \
                         -name '*.png' \) -exec $0 {} +

for file ;do
    echo Process "$file"
done

Then you have to run this with --run as argument.

  • work with any number of files! (Recursively! See maxdepth option)

  • permit many target

  • permit spaces and special characters in file and directrories names

  • you could run same script directly on files, without --run :

     ./myscript hello world 'hello world' Process hello Process world Process hello world

Using pure

Using arrays, you could do things like:

allfiles=( "/drive 1"/images*.{jpg,png} )
[ -f "$allfiles" ] || { echo No file found.; exit ;}

echo Number of files: ${#allfiles[@]}

for file in "${allfiles[@]}";do
    echo Process "$file"
done

There's also a while read loop:

find "/drive1/" -maxdepth 1 -mindepth 1 -type f \( -name '*.jpg' -o -name '*.png' \) |
while IFS= read -r file; do

or with zero terminated files:

find "/drive1/" -maxdepth 1 -mindepth 1 -type f \( -name '*.jpg' -o -name '*.png' \) -print0 |
while IFS= read -r -d '' file; do
     

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM