
この質問は以下に基づいています。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
コマンドを使用して、たとえば標準出力を2つの列に印刷できます。
$ 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ループを使用できますが、シェルのループは非常に遅いです。
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
賞金のお知らせには、次の内容が記載されています。
答えは、行数が異なる変数のすべてのシェルで機能し、動的変数の数に対して機能する必要があります。
厳密に言えば、「すべてのシェルで作業する」ことは不可能です。ほとんどできないことです。何もないCシェルシリーズのシェルとBourneシェル/ POSIXシェルシリーズで作業することは非常に重要です。私はbash、dash(かなり基本的なPOSIX互換シェル)、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互換シェルで動作します1。 2番目のアプローチ()はすべてではありませんが、ほとんどのPOSIX互換シェル2で動作しますが、部分はそうではないかもしれません。いくつかの古いシェルは認識されないかもしれません(完全に?)bashismです。`echo word1 ; echo word2`
echo -e 'word3\nword4'
$(…)
$'…'
解決策がパターンに従う変数名に依存しないことを示すために、意図的に一貫したパターンを持たない変数名を使用します。必要に応じて 、、、 varA
および ( または、、、 および)を 使用できます。varB
varC
varD
var1
var2
var3
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準拠シェルには名前付き配列がないため、すべてサポートされている1つの(名前なし)配列である引数リストを使用します。
for a in "$@"
ループに入ると、それ値のリストは、シェルの引数リストを変更できるように設定されます。- 最初のステップでは、破損したシェルのパラメータリストが使用されます
set --
。 - 通り過ぎるたびに、
- 一時ファイルを作成します。移植性を高めるために
`mktemp`
代わりに使用してください$(mktemp)
(そうでなければ$(…)
一般的に好ましい)。 - パラメータリストにファイル名を追加します。
- 現在のパラメータを書き込みます(例:オリジナルパラメータリスト)をファイルに追加します。
printf
これがすべてのシェルで組み込みコマンドとして使用可能かどうかはわかりませんが、書くことができる (組み込みコマンドまたは外部実行プログラムとして)博物館の外部のすべてのシステムにインストールされます。システムにない場合はprintf
試してみることができますが、echo
次に始まる文字列に注意してください。-
または含めます\
。
- 一時ファイルを作成します。移植性を高めるために
pr
ファイルリストから呼び出されます。-T
ページングを防ぐには、Steeldriver OKを使用してください。- ファイルをマージする
-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 その幅の列に列区切りのためのスペース2つを加えたものです。
____________
1しかし、\前にaを入力しない限り、Cシェルはそれを許可しませんEnter。
2 Cシェルはこれを許可しますが、改行文字をスペースに変換します。 (私が逃した部分があるかもしれません。)
答え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