Deb*_*asu 11 bash shell-script read
我有一个用例,我需要在每次迭代开始时读入多个变量,并将用户的输入读入循环。
我不知道如何探索的解决方案的可能途径-
使用for循环而不是... | while read .........我不知道如何在for循环内分配多个变量
echo -e "1 2 3\n4 5 6" |\
while read a b c;
do
echo "$a -> $b -> $c";
echo "Enter a number:";
read d ;
echo "This number is $d" ;
done
Run Code Online (Sandbox Code Playgroud)ilk*_*chu 12
如果我做对了,我认为您想基本上循环遍历值列表,然后read循环中的另一个。
这里有几个选项,1 和 2 可能是最明智的。
1. 用字符串模拟数组
拥有二维数组会很好,但在 Bash 中不太可能。如果您的值没有空格,一种近似的解决方法是将每组三个数字粘贴到一个字符串中,并在循环内拆分字符串:
for x in "1 2 3" "4 5 6"; do
read a b c <<< "$x";
read -p "Enter a number: " d
echo "$a - $b - $c - $d ";
done
Run Code Online (Sandbox Code Playgroud)
当然,您也可以使用其他一些分隔符,例如for x in 1:2:3 ...和 IFS=: read a b c <<< "$x"。
2. 用另一个重定向替换管道以释放标准输入
另一种可能性是read a b c从另一个 fd 读取并将输入定向到那个(这应该在标准 shell 中工作):
while read a b c <&3; do
printf "Enter a number: "
read d
echo "$a - $b - $c - $d ";
done 3<<EOF
1 2 3
4 5 6
EOF
Run Code Online (Sandbox Code Playgroud)
如果您想从命令中获取数据,也可以在这里使用进程替换:(while read a b c <&3; ...done 3< <(echo $'1 2 3\n4 5 6')进程替换是 bash/ksh/zsh 功能)
3. 取而代之的是从 stderr 获取用户输入
或者,反过来,在您的示例中使用管道,但让用户输入read来自stderr(fd 2) 而不是stdin管道来自何处:
echo $'1 2 3\n4 5 6' |
while read a b c; do
read -u 2 -p "Enter a number: " d
echo "$a - $b - $c - $d ";
done
Run Code Online (Sandbox Code Playgroud)
阅读自stderr有点奇怪,但实际上经常在交互式会话中工作。(您也可以显式地 open /dev/tty,假设您实际上想要绕过任何重定向,less即使数据通过管道传输到它,这也是用于获取用户输入的东西。)
尽管这样使用stderr可能并非在所有情况下都有效,并且如果您使用的是某些外部命令而不是read,则至少需要向该命令添加一堆重定向。
另外,请参阅为什么我的变量在一个“while read”循环中是局部变量,而不是在另一个看似相似的循环中?对于一些问题... | while。
4. 根据需要对数组的部分进行切片
我想您还可以通过复制常规一维数组的切片来近似二维数组:
data=(1 2 3
4 5 6)
n=3
for ((i=0; i < "${#data[@]}"; i += n)); do
a=( "${data[@]:i:n}" )
read -p "Enter a number: " d
echo "${a[0]} - ${a[1]} - ${a[2]} - $d "
done
Run Code Online (Sandbox Code Playgroud)
如果您想要变量的名称,您也可以将${a[0]}etc.分配给a, betc ,但Zsh 会做得更好。
小智 5
只有一个/dev/stdin,read将从使用它的任何地方读取它(默认情况下)。
解决方案是使用其他文件描述符代替 1 ( /dev/stdin)。
从等效代码(在 bash 中)到您发布的内容[1](如下所示),
只需添加0</dev/tty(例如)即可从“真实”tty 读取:
while read a b c
do read -p "Enter a number: " d 0</dev/tty # 0<&2 is also valid
echo "$a -> $b -> $c and ++> $d"
done <<<"$(echo -e '1 2 3\n4 5 6')"
Run Code Online (Sandbox Code Playgroud)
执行时:
$ ./script
Enter a number: 789
1 -> 2 -> 3 and ++> 789
Enter a number: 333
4 -> 5 -> 6 and ++> 333
Run Code Online (Sandbox Code Playgroud)
另一种选择是使用0<&2(这可能看起来很奇怪,但有效)。
请注意,读取/dev/tty(also 0<&2) 将绕过脚本的标准输入,这不会从 echo 读取值:
$ echo -e "33\n44" | ./script
Run Code Online (Sandbox Code Playgroud)
需要的是将一个输入重定向到其他某个 fd(文件描述符)。
在 ksh、bash 和 zsh 中有效:
while read -u 7 a b c
do printf "Enter a number: "
read d
echo "$a -> $b -> $c and ++> $d"
done 7<<<"$(echo -e '1 2 3\n4 5 6')"
Run Code Online (Sandbox Code Playgroud)
或者,使用 exec:
exec 7<<<"$(echo -e '1 2 3\n4 5 6')"
while read -u 7 a b c
do printf "Enter a number: "
read d
echo "$a -> $b -> $c and ++> $d"
done
exec 7>&-
Run Code Online (Sandbox Code Playgroud)
在 sh 中有效的解决方案(<<<无效):
exec 7<<-\_EOT_
1 2 3
4 5 6
_EOT_
while read a b c <&7
do printf "Enter a number: "
read d
echo "$a -> $b -> $c and ++> $d"
done
exec 7>&-
Run Code Online (Sandbox Code Playgroud)
但这可能更容易理解:
while read a b c 0<&7
do printf "Enter a number: "
read d
echo "$a -> $b -> $c and ++> $d"
done 7<<-\_EOT_
1 2 3
4 5 6
_EOT_
Run Code Online (Sandbox Code Playgroud)
你的代码是:
echo -e "1 2 3\n4 5 6" |\
while read a b c;
do
echo "$a -> $b -> $c";
echo "Enter a number: ";
read d ;
echo "This number is $d" ;
done
Run Code Online (Sandbox Code Playgroud)
简化的代码(在 bash 中)是:
while read a b c
do #0</dev/tty
read -p "Enter a number: " d ;
echo "$a -> $b -> $c and ++> $d";
done <<<"$(echo -e '1 2 3\n4 5 6')"
Run Code Online (Sandbox Code Playgroud)
如果执行,将打印:
$ ./script
1 -> 2 -> 3 and ++> 4 5 6
Run Code Online (Sandbox Code Playgroud)
这只是表明 var d 是从同一个/dev/stdin.