2009-11-06 13 views
28

Próbuję napisać skrypt bash, który będzie przetwarzać listę plików, których nazwy są przechowywane w jednej w wierszu pliku wejściowego, coś pokrojuJak mogę odczytać listę nazw plików z pliku w bash?

find . -type f -mtime +15 > /tmp/filelist.txt 
for F in $(cat /tmp/filelist.txt) ; do 
    ... 
done; 

Moim problemem jest to, że nazwy plików w filelist.txt może zawierać przestrzenie, tak wycięte powyżej rozszerzy linię

my text file.txt 

trzech różnych nazw, my, text i file.txt. Jak mogę to naprawić?

+1

Wiesz, wpisy w katalogach mogą również zawierać znaki nowej linii, jak również jako przestrzenie i inne zwariowane postacie. Jedyne rzeczy, których nie może mieć pozycja katalogu (nazwa pliku) to znaki "/" i "". (slash i null). – chris

+0

Tak, ale pliki, nad którymi pracuję, są tworzone przez użytkowników systemu Windows w mojej sieci LAN, która ma dostęp do udziału samby, więc istnieje ograniczenie dziwności nazwy pliku – agnul

Odpowiedz

36

Zastosowanie read:

while read F ; do 
     echo $F 
done </tmp/filelist.txt 

Alternatywnie można użyć IFS, aby zmienić sposób powłoka oddziela swoją listę:

OLDIFS=$IFS 
IFS=" 
" 
for F in $(cat /tmp/filelist.txt) ; do 
    echo $F 
done 
IFS=$OLDIFS 

Alternatywnie (jak sugeruje @tangens), konwersja ciało swojej pętli do oddzielny skrypt, a następnie użyj opcji -exec, aby uruchomić, jeśli dla każdego znalezionego pliku bezpośrednio.

+0

Mimo, że zagłosowałem, stwierdziłem, że ten skrypt nie działa na ścieżkach zawierających znaki spoza ASCII. – kakyo

-1

Wierzę, że można pominąć plik tymczasowy w całości i tylko bezpośrednio iteracyjne nad wynikami znaleźć, tj .:

for F in $(find . -type f -mtime +15) ; do 
    ... 
done; 

ma gwarancji, że moje składnia jest poprawna, ale jestem prawie pewien, że koncepcja działa.

Edycja: Jeśli naprawdę musisz przetworzyć plik z listą nazw plików i nie możesz po prostu połączyć powyższych poleceń, to możesz zmienić wartość zmiennej IFS - oznacza to pole wewnętrzne Separator - aby zmienić sposób, w jaki bash określa pola. Domyślnie jest ustawiona na białe znaki, więc nowa linia, spacja lub tabulacja rozpocznie nowe pole. Jeśli ustawisz tak, by zawierał tylko znak nowej linii, możesz go powtórzyć w taki sam sposób, jak przedtem.

+1

Myślę, że powłoka nadal będzie robić separację na białej przestrzeni? –

+0

Powtórz to - ma problem SAME jako oryginał (dzieli się na spacje). – DVK

+0

Dokonałem edycji i musiałem odebrać telefon w środku, ale myślę, że 5 minut to zbyt długo, aby czekać na tej stronie. – qid

3

stosowanie podczas odczytu

echo $FILE | while read line 
do 
echo $line 
done 

Można zrobić przekierowanie zamiast echo

+0

Jeśli masz tylko jeden plik, to przekierowanie powłoki nie spowoduje utworzenia 'cat' procesu. –

0

Nie jestem ekspertem bash za pomocą jakichkolwiek środków (zwykle napisać skrypt w Ruby lub Python być cross-platform), ale użyłbym wyrażeń regularnych, aby uciec z przestrzeni w każdej linii, zanim ją przetworzysz.

Dla Bash Regex: http://www.linuxjournal.com/node/1006996

w podobnej sytuacji w Ruby (przetwarzanie pliku csv, a sprzątanie każdą linię przed użyciem):

File.foreach(csv_file_name) do |line| 
    clean_line = line.gsub(/()/, '\ ') 
    #this finds the space in your file name and escapes it  
    #do more stuff here 
end 
1

Można użyć parametru -execfind i używać nazw plików bezpośrednio:

find . -type f -mtime +15 -exec <your command here> {} \; 

{} jest symbolem zastępczym dla nazwy pliku.

+0

Problem polega na tym, że nie używam jednego polecenia, a ostatecznie spróbuję wszystkich typów dziwnych kombinacji, by znaleźć właściwy sposób cytowania i uciec z tego po - wylogować się – agnul

6

Można to zrobić bez pliku tymczasowego korzystania podstawienie procesowe:

while read F 
do 
    ... 
done < <(find . -type f -mtime +15) 
+0

Schludnie! Nie wiedziałem o podstawianiu procesu. – agnul

+1

Należy zauważyć, że funkcja podstawiania procesu jest rozszerzeniem bash i nie jest dostępna nawet w bash w trybie zgodności sh. Musisz uruchomić skrypt z '#!/Bin/bash', aby działał. BTW, polecam również użycie 'while IFS =" "read -r F', aby uniknąć możliwych problemów z białymi znakami na początku lub końcu nazw plików i backslashami na końcu (chociaż jeśli pliki pochodzą z Windows, backslashes prawdopodobnie nie są możliwy). –

1

rury Twojego polecenia find prosto do while read pętli

find . -type f -mtime +15 | while read -r line 
do 
    printf "do something with $line\n" 
done 
Powiązane problemy