Na przykład, teraz używam poniższego, aby zmienić kilka plików, których ścieżki uniksowe napisałem do pliku:
cat file.txt | while read in; do chmod 755 "$in"; done
Czy jest bardziej elegancki i bezpieczniejszy sposób?
Dzieje się tak, ponieważ nie ma tylko jednej odpowiedzi ...
shell
rozwinięcie wiersza poleceńxargs
dedykowane narzędziewhile read
z kilkoma uwagamiwhile read -u
użycie dedykowanego fd
, interaktywnego przetwarzania (przykład)Odnośnie żądania OP: działającym chmod
na wszystkich celach wymienionych w pliku , xargs
jest wskazanym narzędziem. Ale w przypadku niektórych innych aplikacji, niewielkiej liczby plików itp.
Jeśli twój plik nie jest zbyt duży i wszystkie pliki są dobrze nazwane (bez spacji lub innych znaków specjalnych, takich jak cudzysłowy), możesz użyć shell
rozwinięcia wiersza poleceń . Po prostu:
chmod 755 $(<file.txt)
W przypadku małej liczby plików (linii) to polecenie jest lżejsze.
xargs
to właściwe narzędzieW przypadku większej liczby plików lub prawie dowolnej liczby wierszy w pliku wejściowym ...
Dla wielu Binutilsw narzędzi, takich jak chown
, chmod
, rm
, cp -t
...
xargs chmod 755 <file.txt
Jeśli masz specjalne znaki i / lub dużo linii w file.txt
.
xargs -0 chmod 755 < <(tr \\n \\0 <file.txt)
jeśli twoje polecenie musi zostać uruchomione dokładnie 1 raz przez wpis:
xargs -0 -n 1 chmod 755 < <(tr \\n \\0 <file.txt)
Nie jest to potrzebne w tym przykładzie, ponieważ chmod
akceptujemy wiele plików jako argument, ale pasuje to do tytułu pytania.
W niektórych przypadkach możesz nawet zdefiniować lokalizację argumentu plikowego w poleceniach generowanych przez xargs
:
xargs -0 -I '{}' -n 1 myWrapper -arg1 -file='{}' wrapCmd < <(tr \\n \\0 <file.txt)
seq 1 5
jako dane wejścioweSpróbuj tego:
xargs -n 1 -I{} echo Blah {} blabla {}.. < <(seq 1 5)
Blah 1 blabla 1..
Blah 2 blabla 2..
Blah 3 blabla 3..
Blah 4 blabla 4..
Blah 5 blabla 5..
Gdzie polecenie jest wykonywane raz w wierszu .
while read
i warianty.Zgodnie z sugestią OP cat file.txt | while read in; do chmod 755 "$in"; done
zadziała, ale są 2 problemy:
cat |
jest bezużytecznym widelcem i
| while ... ;done
stanie się podpowłoką, po której środowisko zniknie ;done
.
Więc można to lepiej napisać:
while read in; do chmod 755 "$in"; done < file.txt
Ale,
Możesz zostać ostrzeżony $IFS
i read
flagi:
help read
read: read [-r] ... [-d delim] ... [name ...] ... Reads a single line from the standard input... The line is split into fields as with word splitting, and the first word is assigned to the first NAME, the second word to the second NAME, and so on... Only the characters found in $IFS are recognized as word delimiters. ... Options: ... -d delim continue until the first character of DELIM is read, rather than newline ... -r do not allow backslashes to escape any characters ... Exit Status: The return code is zero, unless end-of-file is encountered...
W niektórych przypadkach może być konieczne użycie
while IFS= read -r in;do chmod 755 "$in";done <file.txt
Aby uniknąć problemów z obcymi nazwami plików. A może jeśli napotkasz problemy z UTF-8
:
while LANG=C IFS= read -r in ; do chmod 755 "$in";done <file.txt
Kiedy używasz STDIN
do czytania file.txt
, twój skrypt nie może być interaktywny (nie możesz STDIN
już go używać ).
while read -u
, używając dedykowanego fd
.Składnia: while read ...;done <file.txt
przekieruje STDIN
do file.txt
. Oznacza to, że nie będziesz w stanie poradzić sobie z procesem, dopóki nie zakończą.
Jeśli planujesz stworzyć narzędzie interaktywne , musisz unikać używania STDIN
alternatywnego deskryptora pliku i używać go .
Deskryptory plików stałych to: 0
dla STDIN , 1
dla STDOUT i 2
dla STDERR . Możesz je zobaczyć poprzez:
ls -l /dev/fd/
lub
ls -l /proc/self/fd/
Stamtąd musisz wybrać nieużywany numer, od 0
do 63
(więcej, w rzeczywistości, w zależności od sysctl
narzędzia superużytkownika) jako deskryptor pliku :
W tym demo użyję fd 7
:
exec 7<file.txt # Without spaces between `7` and `<`!
ls -l /dev/fd/
Wtedy możesz użyć w read -u 7
ten sposób:
while read -u 7 filename;do
ans=;while [ -z "$ans" ];do
read -p "Process file '$filename' (y/n)? " -sn1 foo
[ "$foo" ]&& [ -z "${foo/[yn]}" ]&& ans=$foo || echo '??'
done
if [ "$ans" = "y" ] ;then
echo Yes
echo "Processing '$filename'."
else
echo No
fi
done 7<file.txt
done
Aby zamknąć fd/7
:
exec 7<&- # This will close file descriptor 7.
ls -l /dev/fd/
Uwaga: zezwalam na przekreśloną wersję, ponieważ ta składnia może być przydatna podczas wykonywania wielu operacji we / wy z procesem równoległym:
mkfifo sshfifo
exec 7> >(ssh -t user@host sh >sshfifo)
exec 6<sshfifo
xargs
początkową budową odpowiadającą na tego rodzaju potrzeby, niektóre funkcje, takie jak budowanie poleceń tak długo, jak to możliwe w obecnym środowisku, aby wywoływaćchmod
w tym przypadku jak najmniej, zmniejszając rozwidlenia zapewniają wydajność.while ;do..done <$file
implie działa 1 fork na 1 plik.xargs
może uruchomić 1 fork dla tysięcy plików ... w niezawodny sposób.cat file.txt | tr \\n \\0 | xargs -0 -n1 chmod 755
tr \\n \\0 <file.txt |xargs -0 [command]
jest około 50% szybsza niż metoda, którą opisałeś.Tak.
W ten sposób możesz uniknąć
cat
procesu.cat
jest prawie zawsze złe w takim celu jak ten. Możesz przeczytać więcej o bezużytecznym użyciu Cat.źródło
cat
jest dobrym pomysłem, ale w tym przypadku, polecenie wskazane jestxargs
chmod
(tj. Naprawdę uruchomić jedno polecenie dla każdej linii w pliku).read -r
czyta pojedynczą linię ze standardowego wejścia (read
bez-r
interpretacji odwrotnych ukośników, nie chcesz tego)”.jeśli masz fajny selektor (na przykład wszystkie pliki .txt w katalogu), możesz zrobić:
bash for loop
lub Twój wariant:
źródło
Jeśli wiesz, że nie masz żadnych białych znaków na wejściu:
Jeśli w ścieżkach mogą znajdować się spacje i jeśli masz GNU xargs:
źródło
xargs
jest solidny. To narzędzie jest bardzo stare, a jego kod jest mocno zmieniany . Jego celem było początkowo zbudowanie linii z uwzględnieniem ograniczeń powłoki (64kchar / wiersz lub coś podobnego). Teraz to narzędzie może pracować z bardzo dużymi plikami i może znacznie zmniejszyć liczbę rozwidleń do ostatecznego polecenia. Zobacz moją odpowiedź i / lubman xargs
.brew install findutils
), a następnie wywołać GNU xargs zgxargs
, np.gxargs chmod 755 < file.txt
Jeśli chcesz uruchomić swoje polecenie równolegle dla każdej linii, możesz użyć GNU Parallel
Każda linia twojego pliku zostanie przekazana do programu jako argument. Domyślnie
parallel
uruchamia tyle wątków, ile liczy Twoje procesory. Ale możesz to określić za pomocą-j
źródło
Widzę, że oznaczyłeś bash, ale Perl byłby również dobrym sposobem, aby to zrobić:
Możesz również zastosować wyrażenie regularne, aby upewnić się, że otrzymujesz właściwe pliki, np. Aby przetwarzać tylko pliki .txt:
Aby „wyświetlić podgląd” tego, co się dzieje, wystarczy zamienić lewe apostrofy na podwójne cudzysłowy i dodać przed
print
:źródło
chmod
funkcjęperl -lpe 'chmod 0755, $_' file.txt
- użyj-l
funkcji „auto-chomp”Możesz także użyć AWK, który daje większą elastyczność w obsłudze pliku
jeśli twój plik ma separator pól, taki jak:
Aby uzyskać tylko pierwsze pole, które robisz
Więcej szczegółów można znaleźć w dokumentacji GNU https://www.gnu.org/software/gawk/manual/html_node/Very-Simple.html#Very-Simple
źródło
Logika odnosi się do wielu innych celów. A jak czytać .sh_history każdego użytkownika z / home / filesystem? A jeśli jest ich tysiąc?
Oto skrypt https://github.com/imvieira/SysAdmin_DevOps_Scripts/blob/master/get_and_run.sh
źródło
Wiem, że jest późno, ale nadal
Jeśli przez przypadek napotkasz plik tekstowy zapisany w systemie Windows z
\r\n
zamiast\n
, możesz się zdezorientować, jeśli twoja komenda ma sth po przeczytanej linii jako argument. Usuń więc\r
na przykład:źródło