我知道我可以通过多种方式解决这个问题,但我想知道是否有一种方法可以仅使用 bash 内置函数来解决这个问题,如果没有,最有效的方法是什么。
我有一个文件,内容如下
AAA
B C DDD
FOO BAR
我的意思只是它有几行,每行可能有也可能没有空格。我想运行这样的命令
cmd AAA "B C DDD" "FOO BAR"
如果我使用cmd $(< file)
我得到
cmd AAA B C DDD FOO BAR
如果我使用cmd "$(< file)"
我得到
cmd "AAA B C DDD FOO BAR"
如何让每一行只处理一个参数?
答案1
便携:
set -f # turn off globbing
IFS='
' # split at newlines only
cmd $(cat <file)
unset IFS
set +f
或者使用子 shell 将IFS
和 选项更改本地化:
( set -f; IFS='
'; exec cmd $(cat <file) )
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[@]}"
答案2
您可以使用临时数组来完成此操作。
设置:
$ cat input
AAA
A B C
DE F
$ cat t.sh
#! /bin/bash
echo "$1"
echo "$2"
echo "$3"
填充数组:
$ IFS=$'\n'; set -f; foo=($(<input))
使用数组:
$ for a in "${foo[@]}" ; do echo "--" "$a" "--" ; done
-- AAA --
-- A B C --
-- DE F --
$ ./t.sh "${foo[@]}"
AAA
A B C
DE F
如果没有临时变量,就无法找到一种方法来做到这一点 - 除非更改IFS
对于 来说并不重要cmd
,在这种情况下:
$ IFS=$'\n'; set -f; cmd $(<input)
应该这样做。
答案3
看起来执行此操作的规范方法bash
是这样的
unset args
while IFS= read -r line; do
args+=("$line")
done < file
cmd "${args[@]}"
或者,如果您的 bash 版本有mapfile
:
mapfile -t args < filename
cmd "${args[@]}"
我能找到的映射文件和 while-read 循环与单行循环之间的唯一区别
(set -f; IFS=$'\n'; cmd $(<file))
前者会将空行转换为空参数,而 one-liner 将忽略空行。在这种情况下,无论如何,单行行为是我更喜欢的,所以它的紧凑性是双重好处。
我会使用IFS=$'\n' cmd $(<file)
,但它不起作用,因为在生效$(<file)
之前被解释为形成命令行。IFS=$'\n'
虽然它在我的情况下不起作用,但我现在了解到,很多工具都支持终止行,null (\000)
而不是newline (\n)
在处理文件名时,这确实使很多事情变得更容易,文件名是这些情况的常见来源:
find / -name '*.config' -print0 | xargs -0 md5
提供完全限定的文件名列表作为 md5 的参数,而不进行任何通配符或插值或其他操作。这导致了非内置解决方案
tr "\n" "\000" <file | xargs -0 cmd
尽管这也忽略了空行,但它确实捕获了只有空格的行。
答案4
old=$IFS
IFS=' #newline
'
array=`cat Submissions` #input the text in this variable
for ... #use parts of variable in the for loop
...
done
IFS=$old
我能找到的最好的方法。正常工作。