在bash中迭代字符串变量行

sch*_*ity 5 grep bash usb variable lsblk

我有一个脚本,我想在其中使用命令列出 USB 设备lsblk

命令:

$ lsblk -o NAME,TRAN,VENDOR,MODEL | grep usb
Run Code Online (Sandbox Code Playgroud)

这导致

sdb   usb    Kingston DataTraveler 2.0
sdc   usb    Kingston DT 101 G2 
Run Code Online (Sandbox Code Playgroud)

我想将结果保存在一个变量中以便以后工作,所以我写

$ usbs=$(lsblk -o NAME,TRAN,VENDOR,MODEL | grep usb)
Run Code Online (Sandbox Code Playgroud)

我期待的是变量usbs将结果存储在两整行中,如上所示。但如果我跑:

for i in ${usbs[@]}; do
  echo $i
done
Run Code Online (Sandbox Code Playgroud)

我把结果分成几个词:

sdb
usb
Kingston
DataTraveler
2.0
sdc
usb
Kingston
DT
101
G2
Run Code Online (Sandbox Code Playgroud)

问题: 有没有办法,使用grep命令,我可以将命令的结果存储为两整行?

我更愿意知道是否有一个简单的解决方案,而不是将结果转储到文件中然后读取它。

jes*_*e_b 10

这是使用readarray/mapfile的好情况:

readarray -t usbs < <(lsblk -o NAME,TRAN,VENDOR,MODEL | grep usb)
Run Code Online (Sandbox Code Playgroud)

这将使用您的输出创建一个数组,其中每一行都被分成它自己的元素。

在您的情况下,它会生成一个数组,如:

usbs=(
'sdb   usb    Kingston DataTraveler 2.0'
'sdc   usb    Kingston DT 101 G2'
)
Run Code Online (Sandbox Code Playgroud)

就像您将整个输出分配给一个变量(而不是数组)一样,它本质上是这样做的:

usbs='sdb   usb    Kingston DataTraveler 2.0
sdc   usb    Kingston DT 101 G2 '
Run Code Online (Sandbox Code Playgroud)

为了使它成为一个数组,您将执行以下操作:

usbs=($(lsblk -o NAME,TRAN,VENDOR,MODEL | grep usb))
Run Code Online (Sandbox Code Playgroud)

但这会使每个由空格分隔的单词成为它自己的元素,相当于:

usbs=(
sdb
usb
Kingston
DataTraveler
2.0
sdc
usb
Kingston
DT
101
G2
)
Run Code Online (Sandbox Code Playgroud)

正如一些评论者所指出的那样,并且通常始终是最佳实践,应该引用变量。在这种情况下,您必须并且通常始终应该引用您的变量。

首先,我想说这不是解决问题的正确方法。这有点像说“你不应该杀人,否则你会进监狱”。

同样,您不引用您的变量,因为否则您将引入安全漏洞。你引用你的变量是因为不引用是错误的(但如果对监狱的恐惧可以提供帮助,为什么不呢)。

  • 斯蒂芬·查泽拉斯

在 的情况下for i in ${usbs[@]}; doi将设置为 中的每个单词(由空格分隔)usbs。如果你引用它喜欢for i in "${usbs[@]}"; do,那么i将被设置为每一个元素usbs作为需要。

  • 很好的答案,但是除非他还将循环更改为“${usbs[@]}”中的“for i”,否则 OP 不会看到此操作;做`。 (5认同)

agc*_*agc 3

问题:有没有一种方法可以使用grep命令将命令结果存储为两整行?

是的,您的分配代码是正确的:

usbs=$(lsblk -o NAME,TRAN,VENDOR,MODEL | grep usb)
Run Code Online (Sandbox Code Playgroud)

这完全符合要求;在单个变量(不是数组)中,它存储由lsblk换行符分隔的两行。但for循环并不是读取该变量的正确工具。循环while要好得多,下面是一个带有虚构数据的示例,因为某些读者可能没有插入任何 USB 设备:

t=$(echo foo bar; echo baz bing;)
while read i ; do echo "$i" ; done <<< "$t"
Run Code Online (Sandbox Code Playgroud)

输出:

foo bar
baz bing
Run Code Online (Sandbox Code Playgroud)

这是一个更短的方法:

xargs -L 1 <<< "$t"
Run Code Online (Sandbox Code Playgroud)

注意:虽然普通POSIX样式变量x不是数组,但bash允许x使用数组表示法进行标识,并且不会抱怨x不是数组。演示:

x=f
echo $x ${x[0]} ${x[@]}
Run Code Online (Sandbox Code Playgroud)

输出:

f f f
Run Code Online (Sandbox Code Playgroud)

x不是数组。如果是的话,这段代码(使用bash 参数转换 赋值运算符)肯定会输出一个数组:A

echo "${x[@]@A}"
Run Code Online (Sandbox Code Playgroud)

...它没有:

x='f'
Run Code Online (Sandbox Code Playgroud)

为了对比,让我们将上面的内容与它一个数组时的样子进行比较。首先创建一个非常相似的数组y,然后使用赋值运算符来显示差异:A

y=(f)
echo "${y[@]@A}"
Run Code Online (Sandbox Code Playgroud)

输出:

declare -a y=([0]="f")
Run Code Online (Sandbox Code Playgroud)