这个问题是基于Ask Ubuntu 上有类似的问题,但bash
我希望在 中有类似的输出sh
。
bash 中没有问题;它按预期工作。
wolf@linux:~$ echo $SHELL
/usr/bin/bash
wolf@linux:~$
wolf@linux:~$ varA='Aug 01
> Aug 16
> Aug 26'
wolf@linux:~$
wolf@linux:~$ varB='04:25
> 03:39
> 10:06'
wolf@linux:~$
wolf@linux:~$ echo "$varA $varB"
Aug 01
Aug 16
Aug 26 04:25
03:39
10:06
wolf@linux:~$
wolf@linux:~$ paste <(printf %s "$varA") <(printf %s "$varB")
Aug 01 04:25
Aug 16 03:39
Aug 26 10:06
wolf@linux:~$
但是,当我在 中尝试类似的命令时sh
,出现以下错误。
wolf@linux:~$ sh
$
$ varA='Aug 01
> Aug 16
> Aug 26'
$ varB='04:25
> 03:39
> 10:06'
$
$ echo "$varA $varB"
Aug 01
Aug 16
Aug 26 04:25
03:39
10:06
$
$ paste <(printf %s "$varA") <(printf %s "$varB")
sh: 22: Syntax error: "(" unexpected
$
是否有可能获得类似的输出sh
?
答案1
如果您的变量具有相同的行数那么您可以使用该pr
命令将标准输出打印到两列中,例如。
$ printf '%s\n' "$varA" "$varB" | pr -2 -Ts^I
Aug 01 04:25
Aug 16 03:39
Aug 26 10:06
其中^I
代表制表符(与命令的默认分隔符一致paste
),可以使用组合键Ctrl+引入V TAB,同时-T
关闭页眉和页脚。
答案2
您始终可以使用它mktemp
,但 POSIX 未指定它,并且存在一些与之相关的竞争条件。
您始终可以使用像这样的 while 循环,但请记住 shell 中的循环非常慢:
counter=1
while true
do
left="$(echo "$varA" | awk -v cnt="$counter" 'NR==cnt {printf $0}')"
right="$(echo "$varB" | awk -v cnt="$counter" 'NR==cnt {printf $0}')"
if [ -z "$left" ] && [ -z "$right" ]
then
break
fi
echo "$left $right"
counter=$((counter + 1))
done
输出:
Aug 01 04:25
Aug 16 03:39
Aug 26 10:06
如果需要,您可以添加更多变量,但它不能满足 应该适用于动态数量的变量要求,因为您必须手动添加它们。当然,您可以在 awk 中实现所有内容。
答案3
赏金通知上写着
对于行数不同的变量,答案应该适用于所有 shell,并且应该适用于动态数量的变量
严格来说,“在所有 shell 上工作”是不可能的。这几乎是不可能做到的任何事物它在 C shell 系列 shell 和 Bourne shell / POSIX 系列 shell 中工作非常重要。我在 bash、dash(这是一个相当基本的 POSIX 兼容 shell)、ash 和 zsh 中测试了以下内容。
以下是设置多行变量的几种方法:
var17='The
quick
brown
fox'
var42=`echo jumps; echo over; echo -e 'the\nlazy\ndog.'`
another_var=$(printf '%s\n' And they lived happily ever after.)
yet_another=$'The\nend.'
第一种方法(实际上Enter在引号内输入)应该适用于大多数(如果不是全部)符合 POSIX 的 shell 1。第二种方法 ( ) 应该适用于大多数(如果不是全部)符合 POSIX 的 shell 2,尽管 ) 部分可能不会。一些旧的 shell 可能无法识别,并且(完全?)是一种 bashism。`echo word1 ; echo word2`
echo -e 'word3\nword4'
$(…)
$'…'
我故意使用没有一致模式的变量名称,以证明该解决方案不依赖于遵循模式的变量名称。如果需要,您可以使用 varA
, varB
, varC
and varD
(或var1
, var2
, var3
and )。var4
运行解决方案:
$ ./myscript1 "$var17" "$var42" "$another_var" "$yet_another"
The jumps And The
quick over they end.
brown the lived
fox lazy happily
dog. ever
after.
请注意显而易见的事情:字符串具有不同的行数,并且行具有不同的字符数。我们不必须 使用变量;我们可以直接在命令行上输入多行字符串值:
$ ./myscript1 "$var17" "$var42" "$another_var" $'The\nend.'
The jumps And The
quick over they end.
brown the lived
fox lazy happily
dog. ever
after.
这是myscript1
:
#!/bin/dash
first=1
for a in "$@"
do
if [ "$first" = 1 ]
then
set --
first=
fi
file=`mktemp`
set -- "$@" "$file"
printf '%s\n' "$a" > "$file"
done
pr -T -m "$@"
rm "$@"
笔记:
- 由于许多符合 POSIX 标准的 shell 没有命名数组,因此我们使用它们都支持的一个(未命名)数组:参数列表。
- 一旦我们进入
for a in "$@"
循环, 它是值列表已设置,因此我们可以更改 shell 的参数列表。 - 第一次通过时,用 破坏 shell 的参数列表
set --
。 - 每次经过,
- 创建一个临时文件。使用
`mktemp`
而不是$(mktemp)
为了增加便携性(否则$(…)
通常是首选)。 - 将文件名添加到参数列表中。
- 写入当前参数(即,来自原来的参数列表)到文件。我不知道是否
printf
可以在所有 shell 中作为内置命令使用,但应该是可用的 (作为内置命令或外部可执行程序)在博物馆外的所有系统上。如果您的系统没有printf
,您可以尝试使用echo
,但要注意以开头的字符串-
或包含\
.
- 创建一个临时文件。使用
pr
在文件列表上 调用。- 使用
-T
Steeldriver 确定的 来防止分页。 - 用于
-m
合并文件(即在多列模式下)。
- 使用
- 最后,删除所有文件。
这看起来像是将列分隔了很多空间,但情况并非总是如此:
$ ./myscript1 "A very long string about a quick brown fox and a lazy dog" .
A very long string about a quick br .
发生的事情是:
pr
计算文件数量;即列数。我们就这样称呼它吧名词- 它将线宽除以,向下舍入。 (线宽默认为 72。)
- 它创建n列,每列 ⌊ 72/n ⌋ 字符位置较宽。比该值更窄的值会被填充;比该值更宽的值将被截断。
您可能更愿意避免空格和截断。我们可以这样做,因为我们可以pr
使用以下 选项指定线宽-w
:
$ ./myscript2 "A very long string about a quick brown fox and a lazy dog" .
A very long string about a quick brown fox and a lazy dog .
这是myscript2
:
#!/bin/dash
if [ "$#" = 0 ]
then
printf '%s\t%s\n' "Usage:" "$0 [string] ..."
exit 1
fi
first=1
maxwidth=0
for a
do
if [ "$first" = 1 ]
then
set --
first=
fi
file=`mktemp`
set -- "$@" "$file"
printf '%s\n' "$a" > "$file"
width=`printf '%s\n' "$a" | wc -L`
if [ "$width" -gt "$maxwidth" ]
then
maxwidth="$width"
fi
done
linewidth=`expr "$#" "*" "(" "$maxwidth" + 2 ")"`
pr -T -w "$linewidth" -m "$@"
rm "$@"
笔记:
for a
是 的缩写for a in "$@"
。这适用于 ash、bash、dash、zsh 以及其他可能的系统。wc -L
查找输入中的最大行长度。不幸的是,这是一个 GNU 扩展。这可以使用 POSIX 工具来完成,但并不那么简单。- 循环找到任何输入中最长的行......
- …然后我们计算足以容纳的线宽n 该宽度的列,加上两个用于列分隔的空格。
____________
1 但是 C shell 不允许这样做,除非您\在 之前键入 a Enter。
2 C shell 允许这样做,但它将换行符转换为空格。 (我可能遗漏了一些东西。)
答案4
1. 命令paste
paste /dev/fd/3 3<<-EOF /dev/fd/4 4<<-EOF
> $varA
> EOF
> $varB
> EOF
输出 :
Aug 01 04:25
Aug 16 03:39
Aug 26 10:06
或者,您也可以将粘贴与临时文件一起使用,但过程很漫长
temp_file=$(mktemp)
2. 命令pr
printf '%s\n' "$varA" "$varB" | pr -2 -t -s
输出:
Aug 01 04:25
Aug 16 03:39
Aug 26 10:06