bash配列変数を改行で区切られた文字列に変換するにはどうすればよいですか?


50

各要素を新しい行に入れて、bash配列変数をファイルに書き出したいと思います。forループでこれを行うことができますが、要素を結合する別の(よりクリーンな)方法はあり\nますか?

回答:


59

これは、bashパラメーター展開とそのIFS特殊変数を利用する方法です。

$ System=('s1' 's2' 's3' 's4 4 4')
$ ( IFS=$'\n'; echo "${System[*]}" )

サブシェルを使用IFSして、現在の環境での値の上書きを回避します。そのサブシェルではIFS、最初の文字が改行になるように値を変更します($'...'引用符を使用)。最後に、パラメーター展開を使用して、配列の内容を1つの単語として出力します。各要素は、の最初の文字で区切られていIFSます。

変数にキャプチャするには:

$ var=$( IFS=$'\n'; echo "${System[*]}" )

bashが十分に新しい(4.2以降)場合、次printf-vオプションを引き続き使用できます(および使用する必要があります)。

$ printf -v var "%s\n" "${System[@]}"

いずれの場合でも、最終的な改行は必要ありませんvar。削除するには:

$ var=${var%?}    # Remove the final character of var

おかげで、これを変数に出力する方法はありますか?
ACyclic

変数にキャプチャする方法を示すために更新されました。
-chepner

2
最後の例はvar=${var%?}代わりにすべきではありませんか?これは正規表現ではないため.、ピリオド文字のみに一致します。
ムシフィル

必要性は、配列変数名を引用:$ IFS=', ' $ echo ${VAR[*]} first second third $ echo "${VAR[*]}" first,second,third
Seff

28

を使用printfして、各配列項目を独自の行に印刷できます。

 $ System=('s1' 's2' 's3' 's4 4 4')
 $ printf "%s\n"  "${System[@]}"
s1
s2
s3
s4 4 4

7
awk -v sep='\n' 'BEGIN{ORS=OFS="";for(i=1;i<ARGC;i++){print ARGV[i],ARGC-i-1?sep:""}}' "${arr[@]}"

または

perl -le 'print join "\n",@ARGV' "${arr[@]}"

または

python -c 'import sys;print "\n".join(sys.argv[1:])' "${arr[@]}"

または

sh -c 'IFS=$'\''\n'\'';echo "$*"' '' "${arr[@]}"

または

lua <(echo 'print(table.concat(arg,"\n"))') "${arr[@]}"

または

tclsh <(echo 'puts [join $argv "\n"]') "${arr[@]}"

または

php -r 'echo implode("\n",array_slice($argv,1));' -- "${arr[@]}"

または

ruby -e 'puts ARGV.join("\n")' "${arr[@]}"

これで思い出せるのはこれだけです。


2

上記の解決策はほとんどそれですが、元の質問はファイルへの出力を求めます:

$ a=(a b c d e)
$ ( IFS=$'\n'; echo "${a[*]}" ) > /tmp/file
$ cat /tmp/file
a
b
c
d
e
$

注:1) 'echo'は最後の改行を提供します2)このファイルがbashによって再び読み込まれる場合、declare -pが必要なシリアル化である可能性があります。


2

forの使用:

for each in "${alpha[@]}"
do
  echo "$each"
done

履歴を使用します。値に次が含まれている場合、これは失敗します!

history -p "${alpha[@]}"

basenameを使用します。値に次が含まれている場合、これは失敗します/

basename -a "${alpha[@]}"

shufを使用します。結果が順番に出ない場合があることに注意してください。

shuf -e "${alpha[@]}"

0

私のテイクは、変更せずにBashビルトインのみを使用しIFSます:

# $1  separator
# $2… strings
join_strings () {
    declare separator="$1";
    declare -a args=("${@:2}");
    declare result;
    printf -v result '%s' "${args[@]/#/$separator}";
    printf '%s' "${result:${#separator}}"
}

$ join_strings $'\n' "a b c" "d e f" "g h i"
a b c
d e f
g h i

任意のセパレータを使用することもできます:

$ join_strings '===' "a b c" "d e f" "g h i"
a b c===d e f===g h i

-1

printf 配列から区切られた文字列を作成するための最も効率的なアプローチのようです:

# create a delimited string; note that printf doesn't put the trailing delimiter
# need to save and restore IFS
# it is prudent to put the whole logic on a single line so as to minimize the risk of future code changes breaking the sequence of saving/restoring of IFS
oldIFS=$IFS; IFS=$'\n'; printf -v var "${arr[*]}"; IFS=$oldIFS

# print string to file; note that the newline is required in the format string because printf wouldn't put a trailing delimiter (which is a good thing)
printf '%s\n' "$var" > file

これを行うさらに簡単な方法は次のとおりです。

delim=$'\n'
printf -v var "%s$delim" "${arr[@]}" # create a delimited string
var="${var%$delim}"                  # remove the trailing delimiter

delim=:
arr=(one two three)
printf -v var "%s$delim" "${arr[@]}" # yields one:two:three:
var="${var%$delim}"                  # yields one:two_three

2
Downvoter、ここで間違っていることをコメントしてください。
コードフォレスター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.