cmd2 `cmd1` 与 cmd1 | xargs cmd2

wes*_*wes 4 pipe xargs shell-script command-substitution

我试图找到一个文件,find然后将找到的路径传递给sqlite3. sqlite>如果我的命令是以下形式,我只会进入提示:

sqlite3 `find . -type f -iname "*.db" | head -n1`
Run Code Online (Sandbox Code Playgroud)

但这些表格不起作用:

find . -type f -iname "*.db" | head -n1 | xargs sqlite3
find . -type f -iname "*.db" | head -n1 | xargs -I% sh -c 'sqlite3 %;'
Run Code Online (Sandbox Code Playgroud)

为什么后面的命令什么都不做?

Sté*_*las 6

在:

... | xargs cmd
Run Code Online (Sandbox Code Playgroud)

根据实现,cmd的标准输入要么是 要么是/dev/null那个管道。它不能是外部标准输入,因为它因管道而丢失。

$ echo /proc/self/fd/0 | gnu-xargs ls -ld
lr-x------ 1 me me 64 Dec 11 22:04 /proc/self/fd/0 -> /dev/null
$ echo /proc/self/fd/0 | busybox-or-solaris-...-xargs ls -ld
lr-x------    1 me me        64 Dec 11 22:04 /proc/self/fd/0 -> pipe:[99839]
Run Code Online (Sandbox Code Playgroud)

无论哪种情况,sqlite3都无法读取您的任何输入。

这:

sqlite3 `find . -type f -iname "*.db" | head -n1`
Run Code Online (Sandbox Code Playgroud)

command 获取由 返回的第一个文件路径的第一行,find根据 将其拆分$IFS,对它们执行通配符,并将结果单词作为不同的参数传递给 sqlite3。那意义不大。

使用 GNU find(可能是您使用的,因为您正在使用-iname):

sqlite3 "$(find . -type f -iname "*.db" -print -quit)"
Run Code Online (Sandbox Code Playgroud)

我们使用-exitto tellfind在打印第一个找到的文件后退出。我们使用现代形式的命令替换$(...),并通过引用命令替换来跳过 split+glob 部分(没有意义)。

您还可以使用(仍然是 GNU 特定的):

find . -type f -iname '*.db' \( -exec sqlite3 {} \; -o -true \) -quit
Run Code Online (Sandbox Code Playgroud)

(尽管您会失去 的退出状态sqlite3)。

zsh

setopt extendedglob # best in ~/.zshrc
sqlite3 ./**/*.(#i)db(D.[1])
Run Code Online (Sandbox Code Playgroud)