35

我想在 Linux 上将所有文件作为单个参数传递,但我无法做到这一点。

这是有效的

ls | sort -n | xargs  -i pdftk  {} cat output combinewd2.pdf

这会为每个命令传递一个参数,但我想要一个命令。

4

9 回答 9

47

使用-I选项:

echo prefix | xargs -I % echo % post

输出:

prefix post
于 2016-11-01T06:42:14.643 回答
22

这是一种方法

pdftk $(ls | sort -n) cat output combinewd2.pdf

或使用反引号

pdftk `ls | sort -n` cat output combinewd2.pdf

例如,如果文件名是 100、2、9、3.14、10、1,则命令将是

pdftk 1 2 3.14 9 10 100 cat output combinewd2.pdf

要处理带有空格或其他特殊字符的文件名,请考虑@joeytwiddle出色答案的这个固定版本(它没有按数字排序,请参阅下面的讨论):

#-- The following will handle special characters, and
#   will sort filenames numerically
#   e.g. filenames 100, 2, 9, 3.14, 10, 1 results in 
#      ./1 ./2 ./3.14 ./9 ./10 ./100
#
find . -maxdepth 1 -type f -print0 |
  sort -k1.3n -z -t '\0' |
  xargs -0 sh -c 'pdftk "$@" cat output combinewd2.pdf' "$0"

xargs 的替代品(特定于 bash)

xargs是一个外部命令,在前面的示例中,它调用sh它反过来调用pdftk.

另一种方法是使用内置函数(mapfile如果可用),或使用位置参数。以下示例使用两个函数,print0_files生成 NUL 终止的文件名和create_pdf调用pdftk

print0_files | create_pdf combinewd2.pdf

函数定义如下

#-- Generate the NUL terminated filenames, numerically sorted
print0_files() {
    find . -maxdepth 1 -type f -print0 |
        sort -k1.3n -z -t '\0'
}
#-- Read NUL terminated filenames using mapfile
create_pdf() {
    mapfile -d ''
    pdftk "${MAPFILE[@]}" cat output "$1"
}
#-- Alternative using positional parameters
create_pdf() {
    local -r pdf=$1
    set --
    while IFS= read -r -d '' f; do set -- "$@" "$f"; done
    pdftk "$@" cat output "$pdf"
}

讨论

正如评论中所指出的,简单的初始答案不适用于包含空格或其他特殊字符的文件名。@joeytwiddle 的答案确实处理了特殊字符,尽管它没有按数字排序

#-- The following will not sort numerically due to ./ prefix,
#   e.g. filenames 100, 2, 9, 3.14, 10, 1 results in 
#      ./1 ./10 ./100 ./2 ./3.14 ./9
#
find . -maxdepth 1 -type f -print0 |
  sort -zn |
  xargs -0 sh -c 'pdftk "$@" cat output combinewd2.pdf' "$0"

./由于每个文件名都以find命令 为前缀,因此它不会按数字排序。某些版本find命令支持-printf '%P\0'不包含./前缀。一个更简单、可移植的修复方法是将-d, --dictionary-order选项添加到sort命令中,以便在比较中仅考虑空格和字母数字字符,但仍可能产生错误的顺序

#-- The following will not sort numerically due to decimals
#   e.g. filenames 100, 2, 9, 3.14, 10, 1 results in 
#      ./1 ./2 ./9 ./10 ./100 ./3.14
#
find . -maxdepth 1 -type f -print0 |
  sort -dzn |
  xargs -0 sh -c 'pdftk "$@" cat output combinewd2.pdf' "$0"

如果文件名包含小数,这可能会导致不正确的数字排序。该sort命令确实允许在排序时对字段进行偏移,sort -k1.3n但如果文件名要尽可能通用,则在定义字段分隔符时必须小心,幸运sort -t '\0'的是指定 NUL 作为字段分隔符,并且该find -print0选项指示要使用 NUL作为文件名之间的分隔符,因此sort -z -t '\0'将 NUL 指定为记录分隔符和字段分隔符——每个文件名都是单个字段记录。./鉴于此,我们可以通过将第一个字段的第三个字符指定为数字排序的起始位置来偏移到单个字段并跳过前缀sort -k1.3n -z -t '\0'

于 2013-02-01T04:23:28.290 回答
14

这很难看,但您可以运行并访问assh -c传递的参数列表,如下所示:xargs"${@}"

ls | sort -n | xargs -d'\n' sh -c 'pdftk "${@}" cat output combinewd2.pdf' "${0}"

最后的额外"${0}"内容就在那里,因为正如sh手册页所说

-c 字符串

如果存在-c选项,则从string读取命令。如果string后面有参数,则将它们分配给位置参数,从$0开始。

为了测试这一点,让我们首先创建一些名称复杂的文件,这些文件会混淆大多数其他解决方案:

$ seq 1 100 | xargs -I{} touch '{} with "spaces"'
$ ls
1 with "spaces"    31 with "spaces"  54 with "spaces"  77 with "spaces"
10 with "spaces"   32 with "spaces"  55 with "spaces"  78 with "spaces"
100 with "spaces"  33 with "spaces"  56 with "spaces"  79 with "spaces"
11 with "spaces"   34 with "spaces"  57 with "spaces"  8 with "spaces"
12 with "spaces"   35 with "spaces"  58 with "spaces"  80 with "spaces"
13 with "spaces"   36 with "spaces"  59 with "spaces"  81 with "spaces"
14 with "spaces"   37 with "spaces"  6 with "spaces"   82 with "spaces"
15 with "spaces"   38 with "spaces"  60 with "spaces"  83 with "spaces"
16 with "spaces"   39 with "spaces"  61 with "spaces"  84 with "spaces"
17 with "spaces"   4 with "spaces"   62 with "spaces"  85 with "spaces"
18 with "spaces"   40 with "spaces"  63 with "spaces"  86 with "spaces"
19 with "spaces"   41 with "spaces"  64 with "spaces"  87 with "spaces"
2 with "spaces"    42 with "spaces"  65 with "spaces"  88 with "spaces"
20 with "spaces"   43 with "spaces"  66 with "spaces"  89 with "spaces"
21 with "spaces"   44 with "spaces"  67 with "spaces"  9 with "spaces"
22 with "spaces"   45 with "spaces"  68 with "spaces"  90 with "spaces"
23 with "spaces"   46 with "spaces"  69 with "spaces"  91 with "spaces"
24 with "spaces"   47 with "spaces"  7 with "spaces"   92 with "spaces"
25 with "spaces"   48 with "spaces"  70 with "spaces"  93 with "spaces"
26 with "spaces"   49 with "spaces"  71 with "spaces"  94 with "spaces"
27 with "spaces"   5 with "spaces"   72 with "spaces"  95 with "spaces"
28 with "spaces"   50 with "spaces"  73 with "spaces"  96 with "spaces"
29 with "spaces"   51 with "spaces"  74 with "spaces"  97 with "spaces"
3 with "spaces"    52 with "spaces"  75 with "spaces"  98 with "spaces"
30 with "spaces"   53 with "spaces"  76 with "spaces"  99 with "spaces"
$  ls | sort -n | xargs -d'\n' sh -c 'set -x; pdftk "${@}" cat output combinewd2.pdf' "${0}"
+ pdftk '1 with "spaces"' '2 with "spaces"' '3 with "spaces"' '4 with "spaces"' '5 with "spaces"' '6 with "spaces"' '7 with "spaces"' '8 with "spaces"' '9 with "spaces"' '10 with "spaces"' '11 with "spaces"' '12 with "spaces"' '13 with "spaces"' '14 with "spaces"' '15 with "spaces"' '16 with "spaces"' '17 with "spaces"' '18 with "spaces"' '19 with "spaces"' '20 with "spaces"' '21 with "spaces"' '22 with "spaces"' '23 with "spaces"' '24 with "spaces"' '25 with "spaces"' '26 with "spaces"' '27 with "spaces"' '28 with "spaces"' '29 with "spaces"' '30 with "spaces"' '31 with "spaces"' '32 with "spaces"' '33 with "spaces"' '34 with "spaces"' '35 with "spaces"' '36 with "spaces"' '37 with "spaces"' '38 with "spaces"' '39 with "spaces"' '40 with "spaces"' '41 with "spaces"' '42 with "spaces"' '43 with "spaces"' '44 with "spaces"' '45 with "spaces"' '46 with "spaces"' '47 with "spaces"' '48 with "spaces"' '49 with "spaces"' '50 with "spaces"' '51 with "spaces"' '52 with "spaces"' '53 with "spaces"' '54 with "spaces"' '55 with "spaces"' '56 with "spaces"' '57 with "spaces"' '58 with "spaces"' '59 with "spaces"' '60 with "spaces"' '61 with "spaces"' '62 with "spaces"' '63 with "spaces"' '64 with "spaces"' '65 with "spaces"' '66 with "spaces"' '67 with "spaces"' '68 with "spaces"' '69 with "spaces"' '70 with "spaces"' '71 with "spaces"' '72 with "spaces"' '73 with "spaces"' '74 with "spaces"' '75 with "spaces"' '76 with "spaces"' '77 with "spaces"' '78 with "spaces"' '79 with "spaces"' '80 with "spaces"' '81 with "spaces"' '82 with "spaces"' '83 with "spaces"' '84 with "spaces"' '85 with "spaces"' '86 with "spaces"' '87 with "spaces"' '88 with "spaces"' '89 with "spaces"' '90 with "spaces"' '91 with "spaces"' '92 with "spaces"' '93 with "spaces"' '94 with "spaces"' '95 with "spaces"' '96 with "spaces"' '97 with "spaces"' '98 with "spaces"' '99 with "spaces"' '100 with "spaces"' cat output combinewd2.pdf

所有论点都被正确引用。请注意,如果任何文件名包含换行符,这将失败,ls -v基本上就是ls | sort -n.

于 2013-02-01T04:54:55.923 回答
8

这应该适用于包含空格、换行符、撇号和引号的文件名(所有这些在 UNIX 文件系统上都是可能的):

find . -maxdepth 1 -type f -print0 |
  sort -zn |
  xargs -0 sh -c 'pdftk "$@" cat output combinewd2.pdf' "$0"

如果您知道您正在使用简单的文件名,那么与接受的答案相比,这可能是过度的。

但是,如果您正在编写一个将来会再次使用的脚本,那么希望它在遇到异常(但有效)输入时不会爆炸。

这基本上是对andrewdotn答案的改编,它以零字节而不是换行符终止输入文件,因此保留包含一个或多个换行符的文件名。

各自的 options -print0-z-0告诉每个程序输入/输出应该由零字节分隔。三个不同的程序,三个不同的论点!

于 2016-02-24T20:14:38.453 回答
7

我发现最直观的方法是:

  • 首先用 -I{} 和“echo”构造命令,
  • 然后用“bash”执行命令(就像你正在执行一个shell脚本)

这是将扩展名从“.txt”重命名为“.txt.json”的示例:

find .|grep txt$|xargs -I{} echo "mv {} {}.json"|bash

将 .txt 重命名为 .json 的稍微高级的示例(删除 .txt 扩展名)

find $PWD|grep txt$|cut -d"." -f1|xargs -I{} echo "mv {}.txt {}.json"|bash

我曾经要求将字符串“End of File”附加到所有文件中。

find .|grep txt|xargs -I{} echo "echo End of File >> {}"|bash

如果你做对了,xargs 就是所有命令之王!

于 2018-11-02T19:42:21.220 回答
5

这是我为同样的问题所做的,并且实际上在生产中使用:

cat chapter_order-pdf-sample.txt | xargs -J % pdftk % cat output sample.pdf
于 2020-02-14T01:27:22.440 回答
3

您可以通过链接两个对 xargs 的调用来做到这一点。使用第一个将所有 args 链接到一个字符串中并将其作为参数传递给echo,第二个 using-I将该 args 链放置到您想要的位置,如下所示:

ls | sort -n | xargs echo | xargs -I {} pdftk {} cat output combinewd2.pdf
于 2018-03-20T16:11:11.660 回答
0

我知道这不是 OP 的问题,但我发现这很有用。

如果你想重新调整你的论点,你可以parallel结合使用xargs.

# limit the number of arguments per line
echo last first middle last first middle | xargs -n 3
last first middle
last first middle

GNU-parallel 现在可以使用参数随意重新排列这些--colsep参数。

echo last first middle last first middle | xargs -n 3 | parallel --colsep=" " echo {2} {3} {1}
first middle last
first middle last

您还可以在其中添加常量参数。

echo last first middle last first middle | xargs -n 3 | parallel --colsep=" " echo {2} {3} middle2 {1}
first middle middle2 last
first middle middle2 last
于 2020-11-18T14:49:44.147 回答
0

不要遵循任何xargs -I解决方案,因为它无法正确处理空白并且无法按预期工作。

TLDR

ls | sort -n | xargs -d '\n' sh -c 'echo pdftk "$@" cat output combinewd2.pdf' sh

对于以下解决方案,我将使用ruby -e 'p ARGV'检查参数,供您参考:

ruby -e 'p ARGV' foo bar buz
["foo", "bar", "buz"]

BSDxargs

如果您使用 BSD 系统(如 macOS),这是最简单的方法。

echo 3 4 | xargs -J@ ruby -e 'p ARGV' 1 2 @ 5 6
["1", "2", "3", "4", "5", "6"]

参考:

-J  replstr
         If this option is specified, xargs will use the data read from
         standard input to replace the first occurrence of replstr instead
         of appending that data after all other arguments.  This option
         will not affect how many arguments will be read from input (-n),
         or the size of the command(s) xargs will generate (-s).  The op-
         tion just moves where those arguments will be placed in the com-
         mand(s) that are executed.  The replstr must show up as a dis-
         tinct argument to xargs.  It will not be recognized if, for in-
         stance, it is in the middle of a quoted string.  Furthermore,
         only the first occurrence of the replstr will be replaced.  For
         example, the following command will copy the list of files and
         directories which start with an uppercase letter in the current
         directory to destdir:

           /bin/ls -1d [A-Z]* | xargs -J % cp -Rp % destdir

GNUxargs

因为没有-Jflag,这里我们创建一个子shell,利用它$@来解决问题。

$@如果您知道如何工作,这可能有点棘手但很容易记住。

echo 3 4 | xargs sh -c 'ruby -e "p ARGV" 1 2 "$@" 5 6' sh
["1", "2", "3", "4", "5", "6"]

请注意,最后一个sh是必需的,否则 $0 将是"3"并且输出将是["1", "2", "4", "5", "6"]. 它不需要,sh但你应该为$0.

-c string

    If the -c option is present, then commands are read from string.
    If there are arguments after the string, they are assigned to the positional
    parameters, starting with $0. 

为什么不-I呢?

因为它不能正确处理空白,例如:

echo  3 4 | xargs -I @ ruby -e 'p ARGV' 1 2 @ 5 6
["1", "2", "3 4", "5", "6"]

另一个例子:

printf "hello world\0goodbye world" | xargs -0 -I @ ruby -e 'p ARGV' before @ after
["before", "hello world", "after"]
["before", "goodbye world", "after"]

使用 sub-shell 和得到预期的输出$@

printf "hello world\0goodbye world" | xargs -0 sh -c 'ruby -e "p ARGV" before "$@" after' sh
["before", "hello world", "goodbye world", "after"]
于 2022-02-22T06:27:07.487 回答