Zapisz zawartość do zmiennej uniksowej z nowymi liniami

117

Mam plik tekstowy test.txt o następującej treści:

text1
text2 

Chcę przypisać zawartość pliku do zmiennej UNIX, ale kiedy to robię:

testvar=$(cat test.txt)
echo $testvar

wynik to:

text1 text2

zamiast

text1
text2 

Czy ktoś może mi zaproponować rozwiązanie tego problemu?

Hugo
źródło

Odpowiedzi:

185

Przypisanie nie usuwa znaków nowej linii, tak naprawdę to echorobi. Aby zachować te nowe linie, wystarczy umieścić cudzysłowy wokół ciągu:

echo "$testvar"

To da pożądany efekt. Zobacz poniższą transkrypcję demonstracji:

pax> cat num1.txt ; x=$(cat num1.txt)
line 1
line 2

pax> echo $x ; echo '===' ; echo "$x"
line 1 line 2
===
line 1
line 2

Powód dlaczego nowe linie są zastępowane spacjami nie jest zupełnie do czynienia z echopoleceniem, raczej jest to kombinacja rzeczy.

Gdy otrzyma wiersz poleceń, bashdzieli go na słowa zgodnie z dokumentacją dla IFSzmiennej:

IFS: wewnętrzny separator pól używany do dzielenia na słowa po interpretacji ... wartością domyślną jest <space><tab><newline>.

Oznacza to, że domyślnie każdy z tych trzech znaków może zostać użyty do podzielenia polecenia na pojedyncze słowa. Po tym, separatory słów zniknęły, pozostała tylko lista słów.

Połącz to z echodokumentacją ( bashpolecenie wewnętrzne), a zobaczysz, dlaczego wypisywane są spacje:

echo [-neE] [arg ...]: wypisuje argumenty oddzielone spacjami, po których następuje znak nowej linii.

Kiedy używasz echo "$x", zmusza całą xzmienną, aby była pojedynczym słowem zgodnie z bash, dlatego nie jest podzielona. Możesz to zobaczyć za pomocą:

pax> function count {
...>    echo $#
...> }
pax> count 1 2 3
3
pax> count a b c d
4
pax> count $x
4
pax> count "$x"
1

Tutaj countfunkcja po prostu wypisuje liczbę podanych argumentów. 1 2 3I a b c dwarianty pokazać go w akcji.

Następnie wypróbowujemy to z dwiema odmianami xzmiennej. Jedna bez cytatów wynika, że istnieją cztery słowa "test", "1", "test"i "2". Dodanie cudzysłowów sprawia, że ​​jest to jedno słowo "test 1\ntest 2".

paxdiablo
źródło
Cytaty ... tego mi brakowało! Dzięki!
anonimowy
Warto również zauważyć: końcowe znaki nowej linii zostaną usunięte przez samego operatora podstawiania poleceń: wypróbuj online!
Jonasza
11

Wynika to ze zmiennej IFS (Internal Field Separator) , która zawiera znak nowej linii.

$ cat xx1
1
2

$ A=`cat xx1`
$ echo $A
1 2

$ echo "|$IFS|"
|       
|

Aby obejść ten problem, należy tymczasowo zresetować IFS, aby nie zawierał nowej linii :

$ IFSBAK=$IFS
$ IFS=" "
$ A=`cat xx1` # Can use $() as well
$ echo $A
1
2
$ IFS=$IFSBAK

Aby ODWRÓCIĆ tę straszną zmianę dla IFS:

IFS=$IFSBAK
DVK
źródło
Dokonałem tej zmiany, ale teraz moje inne rzeczy nie działają, powiedz mi, żebym zresetował?
Pooja25
Jeśli nie potrzebujesz tego, aby przejść do zmiennej, bezpośredniej jednowierszowej:echo "$(IFS=''; cat text.txt)"
Curtis Yallop
3

Jeśli ktoś jest zainteresowany inną opcją:

content=( $(cat test.txt) )

a=0
while [ $a -le ${#content[@]} ]
do
        echo ${content[$a]}
        a=$[a+1]
done
DerWilts
źródło
3

Twoja zmienna jest ustawiona poprawnie przez testvar=$(cat test.txt). Aby wyświetlić tę zmienną, która zawiera znaki nowego wiersza, wystarczy dodać podwójne cudzysłowy, np

echo "$testvar" 

Oto pełny przykład:

$ printf "test1\ntest2" > test.txt
$ testvar=$(<test.txt)
$ grep testvar <(set)
testvar=$'test1\ntest2'
$ echo "$testvar"
text1
text2
$ printf "%b" "$testvar"
text1
text2
kenorb
źródło
0

envdirNarzędzie pozwala w łatwy sposób to zrobić. envdirużywa plików do reprezentowania zmiennych środowiskowych, z mapowaniem nazw plików na nazwy env var i mapowaniem zawartości pliku na wartości env var. Jeśli zawartość pliku zawiera znaki nowej linii, tak samo będzie z env var.

Zobacz https://pypi.python.org/pypi/envdir

Chris Johnson
źródło