命令替换:在换行符而不是空格上拆分

Old*_*Pro 32 bash command-substitution

我知道我可以通过多种方式解决这个问题,但我想知道是否有办法只使用 bash 内置函数来解决这个问题,如果没有,最有效的方法是什么。

我有一个包含类似内容的文件

AAA
B C DDD
FOO BAR
Run Code Online (Sandbox Code Playgroud)

我只是指它有几行,每行可能有也可能没有空格。我想运行一个命令

cmd AAA "B C DDD" "FOO BAR"
Run Code Online (Sandbox Code Playgroud)

如果我使用cmd $(< file)我得到

cmd AAA B C DDD FOO BAR
Run Code Online (Sandbox Code Playgroud)

如果我使用cmd "$(< file)"我得到

cmd "AAA B C DDD FOO BAR"
Run Code Online (Sandbox Code Playgroud)

如何让每一行都处理一个完全相同的参数?

Gil*_*il' 27

便携:

set -f              # turn off globbing
IFS='
'                   # split at newlines only
cmd $(cat <file)
unset IFS
set +f
Run Code Online (Sandbox Code Playgroud)

或者使用子shell 使IFS和选项更改本地:

( set -f; IFS='
'; exec cmd $(cat <file) )
Run Code Online (Sandbox Code Playgroud)

Shell 对不在双引号中的变量或命令替换的结果执行字段拆分和文件名生成。因此,您需要使用 关闭文件名生成set -f,并配置字段拆分IFS以仅使换行符分隔字段。

使用 bash 或 ksh 构造没有什么好处。您可以将IFS函数设为本地,但不能将set -f.

在 bash 或 ksh93 中,如果需要将字段传递给多个命令,则可以将字段存储在数组中。您需要在构建阵列时控制扩展。然后"${a[@]}"扩展到数组的元素,每个单词一个。

set -f; IFS=$'\n'
a=($(cat <file))
set +f; unset IFS
cmd "${a[@]}"
Run Code Online (Sandbox Code Playgroud)


Mat*_*Mat 11

您可以使用临时数组执行此操作。

设置:

$ cat input
AAA
A B C
DE F
$ cat t.sh
#! /bin/bash
echo "$1"
echo "$2"
echo "$3"
Run Code Online (Sandbox Code Playgroud)

填充数组:

$ IFS=$'\n'; set -f; foo=($(<input))
Run Code Online (Sandbox Code Playgroud)

使用数组:

$ for a in "${foo[@]}" ; do echo "--" "$a" "--" ; done
-- AAA --
-- A B C --
-- DE F --

$ ./t.sh "${foo[@]}"
AAA
A B C
DE F
Run Code Online (Sandbox Code Playgroud)

如果没有那个临时变量,无法找到一种方法 - 除非IFS更改对 不重要cmd,在这种情况下:

$ IFS=$'\n'; set -f; cmd $(<input) 
Run Code Online (Sandbox Code Playgroud)

应该这样做。

  • @OldPro `IFS=$'\n' cmd $(&lt;input)` 不起作用,因为它只在 `cmd` 的环境中设置了 `IFS`。`$(&lt;input)` 被扩展以形成命令,然后分配给 `IFS`。 (6认同)

Old*_*Pro 8

看起来这样做的规范方法bash是这样的

unset args
while IFS= read -r line; do 
    args+=("$line") 
done < file

cmd "${args[@]}"
Run Code Online (Sandbox Code Playgroud)

或者,如果您的 bash 版本具有mapfile

mapfile -t args < filename
cmd "${args[@]}"
Run Code Online (Sandbox Code Playgroud)

我能在 mapfile 和 while-read 循环与单行循环之间找到的唯一区别

(set -f; IFS=$'\n'; cmd $(<file))
Run Code Online (Sandbox Code Playgroud)

是前者将空行转换为空参数,而单行将忽略空行。在这种情况下,单行行为无论如何都是我喜欢的,所以它是紧凑的双倍奖励。

我会使用IFS=$'\n' cmd $(<file)但它不起作用,因为$(<file)IFS=$'\n'生效之前被解释为形成命令行。

虽然它在我的情况下不起作用,但我现在了解到很多工具支持终止行null (\000)而不是newline (\n)which 在处理例如文件名时更容易,这是这些情况的常见来源:

find / -name '*.config' -print0 | xargs -0 md5
Run Code Online (Sandbox Code Playgroud)

将完全限定的文件名列表作为参数提供给 md5,没有任何通配或插值或其他任何东西。这导致了非内置解决方案

tr "\n" "\000" <file | xargs -0 cmd
Run Code Online (Sandbox Code Playgroud)

尽管这也忽略了空行,但它确实捕获了只有空格的行。