Jak przekonwertować zmienną tablicy bash na ciąg rozdzielany znakami nowej linii?

50

Chcę zapisać zmienną tablicy bash do pliku, z każdym elementem w nowej linii. Mógłbym to zrobić za pomocą pętli for, ale czy istnieje inny (czystszy) sposób łączenia elementów \n?

Acykliczny
źródło

Odpowiedzi:

59

Oto sposób wykorzystujący rozszerzenie parametrów bash i jego IFSspecjalną zmienną.

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

Używamy podpowłoki, aby uniknąć zastąpienia wartości IFSw bieżącym środowisku. W tej podpowłoce modyfikujemy następnie wartość, IFSaby pierwszy znak był znakiem nowej linii (przy użyciu $'...'cudzysłowu). Na koniec używamy interpretacji parametrów do drukowania zawartości tablicy jako pojedynczego słowa; każdy element jest oddzielony pierwszym charater IFS.

Aby przechwycić zmienną:

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

Jeśli Twój bash jest wystarczająco nowy (4.2 lub nowszy), możesz (i powinieneś) nadal używać printfz -vopcją:

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

W obu przypadkach możesz nie chcieć, aby końcowa nowa linia była w var. Aby go usunąć:

$ var=${var%?}    # Remove the final character of var
chepner
źródło
Dzięki, czy istnieje sposób na wyprowadzenie tego do zmiennej?
Acykliczny
Zaktualizowano, aby pokazać, jak przechwytywać zmienną.
chepner
2
Czy zamiast tego nie powinien być ostatni przykład var=${var%?}? To nie jest wyrażenie regularne, więc .dopasuje tylko znak kropki.
musiphil
Need cytuje nazwę zmiennej tablicowej:$ IFS=', ' $ echo ${VAR[*]} first second third $ echo "${VAR[*]}" first,second,third
Seff
28

Możesz użyć printfdo wydrukowania każdego elementu tablicy w osobnym wierszu:

 $ System=('s1' 's2' 's3' 's4 4 4')
 $ printf "%s\n"  "${System[@]}"
s1
s2
s3
s4 4 4
wysypka
źródło
7
awk -v sep='\n' 'BEGIN{ORS=OFS="";for(i=1;i<ARGC;i++){print ARGV[i],ARGC-i-1?sep:""}}' "${arr[@]}"

lub

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

lub

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

lub

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

lub

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

lub

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

lub

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

lub

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

to wszystko, co do tej pory mogę przypomnieć.

Miauczeć
źródło
2

Powyższe rozwiązania w zasadzie to są, ale pierwotne pytanie dotyczy danych wyjściowych do pliku:

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

Uwagi: 1) „echo” zapewnia ostatnią nową linię 2) Jeśli ten plik zostanie po prostu ponownie odczytany przez bash, to zadeklaruj -p może być pożądaną serializacją.

Brian Chrisman
źródło
2

Używanie do :

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

Korzystanie z historii ; zauważ, że to się nie powiedzie, jeśli twoje wartości zawierają !:

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

Korzystanie z basename ; zauważ, że to się nie powiedzie, jeśli twoje wartości zawierają /:

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

Korzystanie z shuf ; pamiętaj, że wyniki mogą nie zostać wyświetlone w kolejności:

shuf -e "${alpha[@]}"
Steven Penny
źródło
0

Moje podejście , używając tylko wbudowanych wersji Bash, bez mutowania 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}}"
}

Przykład

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

Możesz także użyć dowolnego separatora:

$ join_strings '===' "a b c" "d e f" "g h i"
a b c===d e f===g h i
jcayzac
źródło
-1

printf wydaje się być najbardziej wydajnym podejściem do tworzenia łańcucha rozdzielanego z tablicy:

# 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

Jeszcze prostszym sposobem na to jest:

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

Przykład

delim=:
arr=(one two three)
printf -v var "%s$delim" "${arr[@]}" # yields one:two:three:
var="${var%$delim}"                  # yields one:two_three
codeforester
źródło
2
Downvoter, proszę skomentować, co jest tutaj nie tak.
codeforester