如何使用 xargs 输出到不同的文件名?
How to use xargs to output to different file names?
假设我在列表中有大量文件,就像这样
$ mkdir inputs
$ for i in $(seq 1 1 10000); do printf "$i\n" > inputs/$i; done
$ find inputs/ -type f -exec readlink -f {} \; > files.txt
我想通过如下所示的脚本将它们全部传递
$ cat script.py
#!/usr/bin/env python3
import sys
args = sys.argv[1:]
output_file = args[0]
input_files = args[1:]
text = "got {} files".format(len(input_files))
print(text)
with open(output_file, "w") as fout:
fout.write(text + '\n')
我不能一次全部传递,因为命令行调用太大,系统无法处理。但是,xargs
可以为您解决这个问题;
The command line for command is built up until it reaches a
system-defined limit (unless the -n and -L options are used). The
specified command will be invoked as many times as necessary to use
up the list of input items. In general, there will be many fewer
invocations of command than there were items in the input. This will
normally have significant performance benefits. Some commands can
usefully be executed in parallel too; see the -P option.
你可以像这样看到这个动作;
$ cat files.txt | xargs ./script.py output.txt
got 2151 files
got 2152 files
got 2152 files
got 2152 files
got 1393 files
在这里,xargs
将命令分解为 5 个单独的命令,并且 运行 每个。
但是,输出文件将只有最后一次调用的内容;
$ cat output.txt
got 1393 files
我想要的是获得如下所示的输出文件;
output1.txt # got 2151 files
output2.txt # got 2152 files
output3.txt # got 2152 files
output4.txt # got 2152 files
output5.txt # got 1393 files
有一个问题 here 建议在脚本中完成此操作。但是,我的脚本 script.py
本身无法执行此操作,因为它不知道它在批处理输入集上已经 运行 n
次的事实。在现实生活中,myscript.py
实际上可能是任何我无法修改来完成类似事情的任意第 3 方程序。
因此,如果我可以对 xargs
使用某种参数,它会自动填充已处理的批次的数量 n
,例如
$ cat files.txt | xargs ./script.py output.{n}.txt
有这样的东西吗?是否有一些方法可以使用 xargs
将输入分块到其中的递增批次数来填充命令参数?
这是我可能不得不使用的一种解决方案,直到我想出更好的解决方案;在每个拆分列表上分别预拆分输入文件列表和 运行 xargs
。
$ split -b 130989 files.txt files_split
$ count=0
$ for i in files_split*; do
cat $i | xargs ./script.py output.$count.txt ;
(( count++ ));
done
got 2151 files
got 2152 files
got 2152 files
got 2152 files
got 1396 files
$ ls output*
output.0.txt output.1.txt output.2.txt output.3.txt output.4.txt
$ cat output.*
got 2151 files
got 2152 files
got 2152 files
got 2152 files
got 1396 files
至于号码130989
,我是从$ head -2151 files.txt | wc -c
那里得到的,2151
是xargs
最初分裂的号码。
编辑:看起来您实际上可以从 xargs --show-limits
中更轻松地获得这些数字
我希望其他人可能有更优雅的解决方案。
这是我发现的另一种使用 GNU parallel 而不是 xargs 的方法;
$ parallel -a files.txt --xargs ./script.py output.{#}.txt {}
got 1041 files
got 1041 files
got 1041 files
got 1041 files
got 1041 files
got 631 files
got 1041 files
got 1041 files
got 1041 files
got 1041 files
$ ls -1 output.*
output.10.txt
output.1.txt
output.2.txt
output.3.txt
output.4.txt
output.5.txt
output.6.txt
output.7.txt
output.8.txt
output.9.txt
$ cat output.*
got 631 files
got 1041 files
got 1041 files
got 1041 files
got 1041 files
got 1041 files
got 1041 files
got 1041 files
got 1041 files
got 1041 files
当且仅当您是输入文件的命名者,因此可以确定它们不包含任何花哨的字符(即任何可能破坏内容的字符)或者,最糟糕的是,被恶意利用)那么这可能会回答你的问题:
xargs -a files.txt echo ./script.py output.NNN.txt \
| awk 'gsub("NNN", ++n, )' \
| sh
这里xargs
只生成命令行。然后 awk
用实际数字替换 NNN
并将结果发送到 sh
执行该行(这就是文件名必须绝对安全的原因:落入坏人之手,这可能会造成严重破坏)
注意:awk
也可以在命令末尾添加一个&
,以便并行化。
假设我在列表中有大量文件,就像这样
$ mkdir inputs
$ for i in $(seq 1 1 10000); do printf "$i\n" > inputs/$i; done
$ find inputs/ -type f -exec readlink -f {} \; > files.txt
我想通过如下所示的脚本将它们全部传递
$ cat script.py
#!/usr/bin/env python3
import sys
args = sys.argv[1:]
output_file = args[0]
input_files = args[1:]
text = "got {} files".format(len(input_files))
print(text)
with open(output_file, "w") as fout:
fout.write(text + '\n')
我不能一次全部传递,因为命令行调用太大,系统无法处理。但是,xargs
可以为您解决这个问题;
The command line for command is built up until it reaches a system-defined limit (unless the -n and -L options are used). The specified command will be invoked as many times as necessary to use up the list of input items. In general, there will be many fewer invocations of command than there were items in the input. This will normally have significant performance benefits. Some commands can usefully be executed in parallel too; see the -P option.
你可以像这样看到这个动作;
$ cat files.txt | xargs ./script.py output.txt
got 2151 files
got 2152 files
got 2152 files
got 2152 files
got 1393 files
在这里,xargs
将命令分解为 5 个单独的命令,并且 运行 每个。
但是,输出文件将只有最后一次调用的内容;
$ cat output.txt
got 1393 files
我想要的是获得如下所示的输出文件;
output1.txt # got 2151 files
output2.txt # got 2152 files
output3.txt # got 2152 files
output4.txt # got 2152 files
output5.txt # got 1393 files
有一个问题 here 建议在脚本中完成此操作。但是,我的脚本 script.py
本身无法执行此操作,因为它不知道它在批处理输入集上已经 运行 n
次的事实。在现实生活中,myscript.py
实际上可能是任何我无法修改来完成类似事情的任意第 3 方程序。
因此,如果我可以对 xargs
使用某种参数,它会自动填充已处理的批次的数量 n
,例如
$ cat files.txt | xargs ./script.py output.{n}.txt
有这样的东西吗?是否有一些方法可以使用 xargs
将输入分块到其中的递增批次数来填充命令参数?
这是我可能不得不使用的一种解决方案,直到我想出更好的解决方案;在每个拆分列表上分别预拆分输入文件列表和 运行 xargs
。
$ split -b 130989 files.txt files_split
$ count=0
$ for i in files_split*; do
cat $i | xargs ./script.py output.$count.txt ;
(( count++ ));
done
got 2151 files
got 2152 files
got 2152 files
got 2152 files
got 1396 files
$ ls output*
output.0.txt output.1.txt output.2.txt output.3.txt output.4.txt
$ cat output.*
got 2151 files
got 2152 files
got 2152 files
got 2152 files
got 1396 files
至于号码130989
,我是从$ head -2151 files.txt | wc -c
那里得到的,2151
是xargs
最初分裂的号码。
编辑:看起来您实际上可以从 xargs --show-limits
我希望其他人可能有更优雅的解决方案。
这是我发现的另一种使用 GNU parallel 而不是 xargs 的方法;
$ parallel -a files.txt --xargs ./script.py output.{#}.txt {}
got 1041 files
got 1041 files
got 1041 files
got 1041 files
got 1041 files
got 631 files
got 1041 files
got 1041 files
got 1041 files
got 1041 files
$ ls -1 output.*
output.10.txt
output.1.txt
output.2.txt
output.3.txt
output.4.txt
output.5.txt
output.6.txt
output.7.txt
output.8.txt
output.9.txt
$ cat output.*
got 631 files
got 1041 files
got 1041 files
got 1041 files
got 1041 files
got 1041 files
got 1041 files
got 1041 files
got 1041 files
got 1041 files
当且仅当您是输入文件的命名者,因此可以确定它们不包含任何花哨的字符(即任何可能破坏内容的字符)或者,最糟糕的是,被恶意利用)那么这可能会回答你的问题:
xargs -a files.txt echo ./script.py output.NNN.txt \
| awk 'gsub("NNN", ++n, )' \
| sh
这里xargs
只生成命令行。然后 awk
用实际数字替换 NNN
并将结果发送到 sh
执行该行(这就是文件名必须绝对安全的原因:落入坏人之手,这可能会造成严重破坏)
注意:awk
也可以在命令末尾添加一个&
,以便并行化。