我是 bash 和编码的超级初学者,所以请耐心等待。我有一个文件列表(>1000),我需要将它们转换为另一种格式。但是我需要一次对 40 个文件运行该命令。
这是我尝试过的(但它基本上运行命令,因为之前什么都没有, xargs 不起作用)。
path=/home/dir1/dir2/dir3;
ls ${path} >> ${path}/LIST;
FILES=${path}/LIST;
xargs -n 40 <<$FILES | xargs commandname
答案1
您的问题在于阅读您的列表:
FILES=${path}/LIST;
应该
FILES=$(<"${path}/LIST")
然而,
- 您也可以
xargs
直接读取文件:xargs -a "${path}/LIST"
. xargs
您根本不需要重复。- 您应该始终双引号您的文件名变量!
- 如果您的路径包含空格或换行符,您的脚本就会出现很多问题。
- 添加
-r
到xargs
以防止在未找到文件的情况下运行不带参数的命令。
无论如何,
您应该使用数组代替。
另外,最好习惯将\0
和 一起用作分隔符,以xargs -0
防止换行符作为文件名的一部分出现问题。
shopt -s nullglob # avoid `*` as file if no files found in $path
path=/home/dir1/dir2/dir3
files=("${path}"/*)
printf '%s\0' "${files[@]}" | xargs -0 -r -n40 commandname
shopt -u nullglob
或者使用parallel
代替xargs
:
shopt -s nullglob
parallel -j1 -n40 commandname ::: /home/dir1/dir2/dir3/*
答案2
用zsh
代替bash
.
autoload zargs
zargs -rl40 -- /home/dir1/dir2/dir3/*(nN) -- commandname
(n
对于numericglobsort
排序顺序,通常对于编号文件更好(因此file10
要排序后 file2
对于实例)和N
for (因此,如果匹配,nullglob
则不会抱怨,并且在不包含任何非隐藏文件或不可读时归档))。/home/dir1/dir2/dir3/*
/home/dir1/dir2/dir3
答案3
我认为这样的东西可以工作,而不需要使用 GNU 扩展。
set -- # clear positional arguments
for file in /home/dir1/dir2/dir3/*
do
[ -e "$file" ] || continue
set -- "$@" "$file"
if [ "$#" -eq 40 ]
then
commandname "$@"
set --
fi
done
# in case the number of files is a number not divisible by 40
if [ "$#" -gt 0 ] && [ "$#" -lt 40 ]
then
commandname "$@"
fi
我不知道命令是什么,但是当我做了一个类似的函数时
commandname()
{
printf 'received %d arguments\n' "$#"
printf 'listing received filenames\n'
counter=0
for par in "$@"
do
counter="$(( counter+1 ))"
printf 'argument number %d: %s' "$counter" "$par" | LC_ALL=POSIX tr -d '[:cntrl:]'
printf '\n'
done
}
据我所知,它显示了收到的适当数量的邮件,并正确列出了所有邮件。我对此处列出的大多数文件名进行了测试如何测试 shell 脚本的文件处理稳健性?(53 个文件)。
还值得注意的是,这不会找到隐藏文件(以点开头的文件),但您在 OP 中编写的内容也找不到,所以我认为这很好。如果您只需要常规文件,请更改-e "$file" ]
为[ -f "$file" ]
.
答案4
find /home/dir1/dir2/dir3 -maxdepth 1 -print0 |
xargs -0 -n 40 -P 0 commandname
请注意,如果文件数不是 40 的倍数,则其中一个commandname
进程将无法使用 40 个参数运行。