list=`ls -a R*`
echo $list
Wewnątrz skryptu powłoki to polecenie echo wyświetli wszystkie pliki z bieżącego katalogu zaczynające się na R, ale w jednym wierszu. Jak mogę wydrukować każdy element w jednym wierszu?
Muszę rodzajowe polecenia dla wszystkich scenariuszy dzieje z ls
, du
, find -type -d
, itd.
Odpowiedzi:
Jeśli dane wyjściowe polecenia zawierają wiele wierszy, wówczas podaj zmienne, aby zachować te nowe wiersze podczas echa:
W przeciwnym razie powłoka rozszerzy się i podzieli zawartość zmiennej na białe znaki (spacje, znaki nowej linii, tabulatory) i zostaną utracone.
źródło
Zamiast niewłaściwie umieszczać
ls
dane wyjściowe w zmiennej, a następnieecho
je przetwarzać, co usuwa wszystkie kolory, użyjOd
man ls
Nie radzę nic robić z wyjściem
ls
, z wyjątkiem wyświetlania :)Użyj na przykład globusów powłoki i
for
pętli, aby zrobić coś z plikami ...* To nie będzie zawierała bieżącego katalogu
.
lub jego rodzica..
choćźródło
echo "$i"
zprintf "%s\n" "$i"
(że nie będzie dusić jeśli nazwa pliku zaczyna się od „-”). Właściwie większość czasuecho something
należy zastąpićprintf "%s\n" "something"
.R
tutaj, ale ogólnie tak. Jednak nawet dla skryptów, próbując zawsze pomieścić wiodącym-
w ścieżkach powoduje kłopoty: ludzie zakładają--
, które obsługują tylko niektóre polecenia, oznacza koniec opcji. Widziałem,find . [tests] -exec [cmd] -- {} \;
gdzie[cmd]
nie obsługuje--
. W.
każdym razie każda ścieżka zaczyna się od ! Ale to nie jest argument przeciwko zastąpieniuecho
goprintf '%s\n'
. Tutaj można zastąpić całą pętlęprintf '%s\n' R/*
. Bash maprintf
wbudowane narzędzie, więc nie ma ograniczenia liczby / długości argumentów.Chociaż umieszczanie go w cudzysłowie, jak sugeruje @muru , rzeczywiście zrobi to, o co prosiłeś, możesz również rozważyć użycie tablicy do tego. Na przykład:
IFS=$'\n'
Mówi bash tylko podzielić wyjście na nowej linii characcters o dostać każdy element tablicy. Bez tego podzieli się na spacje, więca file name with spaces.txt
będzie 5 osobnych elementów zamiast jednego. Takie podejście zostanie jednak przerwane, jeśli nazwy plików / katalogów mogą zawierać znaki nowej linii (\n
). Zapisuje każdy wiersz wyniku polecenia jako element tablicy.Zauważ, że zmieniłem również stary styl,
`command`
na$(command)
który jest preferowaną składnią.Masz teraz tablicę o nazwie
$dirs
, każdy bof, którego elementy są wierszem wyjścia poprzedniego polecenia. Na przykład:Teraz, z powodu pewnej dziwności bashu (patrz tutaj ),
IFS
po wykonaniu tej czynności konieczne będzie przywrócenie pierwotnej wartości. Więc albo zapisz to i ponownie przypisz:Lub zrób to ręcznie:
Alternatywnie, po prostu zamknij obecny terminal. Twój nowy będzie miał ponownie ustawiony oryginalny IFS.
źródło
du
tym, jak wygląda OP, jest bardziej zainteresowana zachowaniem formatu wyjściowego.Jeśli musisz przechowywać dane wyjściowe i chcesz mieć tablicę,
mapfile
ułatwia to.Najpierw zastanów się, czy w ogóle musisz przechowywać dane wyjściowe polecenia. Jeśli nie, po prostu uruchom polecenie.
Jeśli zdecydujesz, że chcesz odczytać wynik polecenia jako tablicę wierszy, prawdą jest, że jednym ze sposobów jest wyłączenie globowania, ustawienie
IFS
podziału na linie, stosowanie podstawiania poleceń w(
)
składni tworzenia tablicy, aIFS
następnie resetowanie , co jest bardziej złożone niż się wydaje. Odpowiedź terdona obejmuje niektóre z tych podejść. Ale proponuję użyć zamiast tego wbudowanego poleceniamapfile
powłoki Bash do odczytu tekstu jako tablicy wierszy. Oto prosty przypadek, w którym czytasz z pliku:To odczytuje wiersze do tablicy o nazwie
MAPFILE
. Bez przekierowania wejścia czytałbyś ze standardowego wejścia powłoki (zwykle z terminala) zamiast z pliku o nazwie . Aby wczytać tablicę inną niż domyślna , przekaż jej nazwę. Na przykład wczytuje to do tablicy :< filename
filename
MAPFILE
lines
Innym domyślnym zachowaniem, które możesz zmienić, jest pozostawienie znaków nowej linii na końcach linii; pojawiają się jako ostatni znak w każdym elemencie tablicy (chyba że dane wejściowe zakończyły się bez znaku nowej linii, w którym to przypadku ostatni element nie ma). Aby przełamać te znaki nowej linii, aby nie pojawiły się w tablicy, przekaż
-t
opcję domapfile
. Na przykład odczytuje to tablicęrecords
i nie zapisuje końcowych znaków nowego wiersza do jej elementów tablicy:Możesz także używać
-t
bez przekazywania nazwy tablicy; to znaczy, działa również z niejawną nazwą tablicyMAPFILE
.mapfile
Powłoka wbudowane podpór innych opcji, a to może alternatywnie być powoływane jakoreadarray
. Uruchomhelp mapfile
(ihelp readarray
), aby uzyskać szczegółowe informacje.Ale nie chcesz czytać z pliku, chcesz czytać z wyniku polecenia. Aby to osiągnąć, użyj substytucji procesu . Ta komenda czyta wiersze z komendy
some-command
zarguments...
jak jego argumenty wiersza polecenia i miejsc w nich wmapfile
„s domyślnej tablicyMAPFILE
, z ich zakończeń znaków nowej linii usunięte:Podstawienie procesu zastępowane jest rzeczywistą nazwą pliku, z którego można odczytać wynik działania. Plik jest nazwanym potokiem, a nie zwykłym plikiem, a na Ubuntu zostanie nazwany jak (czasem z inną liczbą niż ), ale nie musisz zajmować się szczegółami, ponieważ dba o to powłoka wszystko za kulisami.
<(some-command arguments...)
some-command arguments...
/dev/fd/63
63
Możesz pomyśleć, że możesz zrezygnować z zastępowania procesów za pomocą zamiast tego, ale to nie zadziała, ponieważ gdy masz wiele potoków oddzielonych , Bash uruchamia wszystkie polecenia w podpowłokach . Zatem zarówno i uruchomić w swoich środowiskach , ale z zainicjowany oddzielić od środowiska powłoki, w którym uruchomiono gazociąg. W podpowłoce, w której działa, tablica jest zapełniana, ale tablica ta jest odrzucana po zakończeniu polecenia. nigdy nie jest tworzony ani modyfikowany dla osoby dzwoniącej.
some-command arguments... | mapfile -t
|
some-command arguments...
mapfile -t
mapfile -t
MAPFILE
MAPFILE
Oto jak wygląda przykład w odpowiedzi terdona , jeśli użyjesz
mapfile
:Otóż to. Nie musisz sprawdzać, czy
IFS
został ustawiony , śledzić, czy nie został ustawiony i jaką wartością ustawić nową linię, a następnie zresetować lub zresetować później. Nie trzeba wyłączać masek (na przykład, zset -f
) - co jest naprawdę potrzebne, jeśli chcesz używać tej metody poważnie, ponieważ nazwy plików mogą zawierać*
,?
oraz[
--then ponownie włączyć go (set +f
) potem.Możesz także całkowicie zastąpić tę konkretną pętlę pojedynczym
printf
poleceniem - choć tak naprawdę nie jest to zaletąmapfile
, ponieważ możesz to zrobić niezależnie od tego, czy użyjeszmapfile
innej metody do zapełnienia tablicy. Oto krótsza wersja:Ważne jest, aby pamiętać, że, jak wspomina terdon , operacja linia po linii nie zawsze jest odpowiednia i nie będzie działać poprawnie z nazwami plików, które zawierają znaki nowej linii. Odradzam nazywanie plików w ten sposób, ale może się to zdarzyć, w tym przez przypadek.
Tak naprawdę nie ma jednego uniwersalnego rozwiązania.
Prosiłeś o „ogólne polecenie dla wszystkich scenariuszy” i podejście
mapfile
polegające na tym, że w pewnym stopniu zbliżasz się do tego celu, ale zachęcam do ponownego rozważenia swoich wymagań.Zadanie pokazane powyżej lepiej wykonać za pomocą jednego
find
polecenia:Możesz także użyć zewnętrznego polecenia, takiego jak
sed
dodaćDIR:
na początku każdej linii. Jest to prawdopodobnie nieco brzydkie i w przeciwieństwie do polecenia find doda dodatkowe „przedrostki” w nazwach plików zawierających znaki nowej linii, ale działa niezależnie od danych wejściowych, więc spełnia twoje wymagania i nadal lepiej jest czytać dane wyjściowe w pliku zmienna lub tablica :Jeśli chcesz wyświetlić listę, a także wykonać akcję na każdym znalezionym katalogu, na przykład uruchomić
some-command
i przekazać ścieżkę katalogu jako argument,find
możesz to zrobić również:Jako kolejny przykład wróćmy do ogólnego zadania dodawania prefiksu do każdej linii. Załóżmy, że chcę zobaczyć wynik,
help mapfile
ale numeruje linie. Tak naprawdę nie użyłbymmapfile
do tego ani żadnej innej metody, która odczytuje go do zmiennej powłoki lub tablicy powłoki. Załóżmy,help mapfile | cat -n
że nie daje pożądanego formatowania, mogę użyćawk
:Odczytywanie wszystkich danych wyjściowych polecenia do jednej zmiennej lub tablicy jest czasem przydatne i odpowiednie, ale ma poważne wady. Nie tylko musisz poradzić sobie z dodatkową złożonością korzystania z powłoki, gdy istniejące polecenie lub kombinacja istniejących poleceń może już robić to, czego potrzebujesz, lub lepiej, ale całe wyjście polecenia musi być przechowywane w pamięci. Czasami możesz wiedzieć, że to nie jest problem, ale czasami możesz przetwarzać duży plik.
Alternatywą, która jest często podejmowana - a czasem wykonywana właściwie - jest odczytywanie wejścia wiersz po wierszu za
read -r
pomocą pętli. Jeśli nie musisz zapisywać poprzednich wierszy podczas pracy na późniejszych wierszach i musisz użyć długich danych wejściowych, może to być lepsze niżmapfile
. Ale również tego należy unikać w przypadkach, gdy można po prostu potokować go do polecenia, które może wykonać pracę, co jest w większości przypadków .źródło