Chcę odczytać plik i zapisać go w zmiennej, ale muszę zachować zmienną, a nie tylko wydrukować plik. W jaki sposób mogę to zrobić? Napisałem ten skrypt, ale nie jest to dokładnie to, czego potrzebowałem:
#!/bin/sh
while read LINE
do
echo $LINE
done <$1
echo 11111-----------
echo $LINE
W moim skrypcie mogę podać nazwę pliku jako parametr, więc jeśli plik zawiera na przykład „aaaa”, wydrukuje to:
aaaa
11111-----
Ale to po prostu drukuje plik na ekranie i chcę zapisać go w zmiennej! Czy jest na to łatwy sposób?
cat
lub$(<someFile)
spowoduje niekompletne wyjście (rozmiar jest mniejszy niż rzeczywisty plik).Odpowiedzi:
W wieloplatformowym, najniższym wspólnym mianowniku
sh
używasz:W
bash
lubzsh
, aby odczytać cały plik do zmiennej bez wywoływaniacat
:Powołując
cat
siębash
lubzsh
slurp plik zostanie uznany za Useless Korzystanie z Kat .Zauważ, że nie jest konieczne cytowanie podstawienia polecenia, aby zachować nowe wiersze.
Patrz: Wiki Bash Hackera - Zastępowanie poleceń - Specjalizacje .
źródło
value="`cat config.txt`"
ivalue="$(<config.txt)"
byłoby bezpieczniejsze, gdyby config.txt zawiera spacje?cat
jak wyżej nie zawsze jest uważane za bezużyteczne użyciecat
. Na przykład< invalid-file 2>/dev/null
spowoduje wyświetlenie komunikatu o błędzie, do którego nie można skierować/dev/null
, a do któregocat invalid-file 2>/dev/null
zostanie poprawnie skierowany/dev/null
.value=$(<config.txt)
to dobrze, alevalue = $(<config.txt)
źle. Uważaj na te miejsca.Jeśli chcesz odczytać cały plik do zmiennej:
Jeśli chcesz to przeczytać wiersz po wierszu:
źródło
echo "$value"
. W przeciwnym razie powłoka wykona tokenizację białych znaków i rozwinięcie wartości wieloznacznej na wartości.read -r
zamiast po prosturead
- zawsze, chyba że wyraźnie potrzebujesz dziwnego, starszego zachowania, o którym wspominasz.Dwie ważne pułapki
które zostały dotychczas zignorowane przez inne odpowiedzi:
Końcowe usuwanie nowego wiersza z rozszerzenia poleceń
Jest to problem dla:
rozwiązania typu, ale nie dla
read
rozwiązań opartych.Rozszerzenie poleceń usuwa końcowe znaki nowej linii:
Wyjścia:
To łamie naiwną metodę czytania z plików:
Obejście POSIX: dodaj dodatkowy znak do rozszerzenia polecenia i usuń go później:
Wyjścia:
Obejście prawie POSIX: kodowanie ASCII. Patrz poniżej.
Usuwanie znaków NUL
Nie ma rozsądnego sposobu na przechowywanie znaków NUL w zmiennych .
Wpływa to zarówno na rozwój, jak i na
read
rozwiązania, i nie znam na to żadnego dobrego obejścia.Przykład:
Wyjścia:
Ha, nasz NUL zniknął!
Obejścia:
Kodowanie ASCII. Patrz poniżej.
użyj
$""
literałów rozszerzenia bash :Działa tylko z literałami, więc nie jest przydatny do czytania z plików.
Obejście problemów
Przechowuj wersję pliku zakodowaną w formacie uuencode base64 w zmiennej i dekoduj przed każdym użyciem:
Wynik:
uuencode i udecode są POSIX 7, ale domyślnie nie są dostępne w Ubuntu 12.04 (
sharutils
pakiet) ... Nie widzę alternatywy POSIX 7 dla<()
rozszerzenia podstawiania procesu bash oprócz zapisu do innego pliku ...Oczywiście jest to powolne i niewygodne, więc sądzę, że prawdziwa odpowiedź brzmi: nie używaj Bash, jeśli plik wejściowy może zawierać znaki NUL.
źródło
S="$(printf "\\\'\"")"; echo $S
. Wyjście:\'"
. Więc to działa =)$()
rozszerzenia, mój przykład pokazuje, że$()
rozszerzenie działa\'"
.to działa dla mnie:
v=$(cat <file_path>) echo $v
źródło
Jak zauważa Ciro Santilli, stosowanie podstawień poleceń spowoduje usunięcie końcowych znaków nowej linii. Ich obejście polegające na dodawaniu końcowych znaków jest świetne, ale po dłuższym używaniu zdecydowałem, że potrzebuję rozwiązania, które w ogóle nie używałoby podstawiania poleceń.
Moje podejście wykorzystuje teraz
read
wraz zprintf
wbudowaną-v
flagą do odczytu zawartości standardowego wejścia bezpośrednio do zmiennej.Obsługuje dane wejściowe z końcowymi znakami nowej linii lub bez.
Przykładowe użycie:
źródło
_contents="${_contents}${_line}\n "
aby zachować nowe linie?$'\n'
? To konieczne, w przeciwnym razie dodajesz dosłowność\
in
znaki. Twój blok kodu ma również dodatkową spację na końcu, nie jestem pewien, czy to celowe, ale wciskałby każdą kolejną linię z dodatkową spacją.Możesz uzyskać dostęp do 1 linii jednocześnie przez pętlę for
Skopiuj całą zawartość do pliku (powiedzmy line.sh); Wykonać
źródło
for
pętla nie zapętla się nad liniami, zapętla się nad słowami. W przypadku/etc/passwd
, zdarza się, że każdy wiersz zawiera tylko jedno słowo. Jednak inne pliki mogą zawierać wiele słów w wierszu.