2009-10-30 10 views
6

Próbuję przeglądać katalog plików tekstowych i łączyć je w jeden dokument. Działa to świetnie, ale pliki tekstowe zawierają fragmenty kodu, a całe moje formatowanie jest zwinięte w lewo. Wszystkie wiodące spacje na linii są usuwane.Zachowanie wiodącej białej przestrzeni podczas czytania >> zapisywanie pliku wiersz po wierszu w bash

#!/bin/sh 
OUTPUT="../best_practices.textile" 
FILES="../best-practices/*.textile" 
for f in "$FILES" 
do 
    echo "Processing $f file..." 
    echo "">$OUTPUT 

    cat $f | while read line; do 
     echo "$line">>$OUTPUT 
    done 
    echo >>$OUTPUT 
    echo >>$OUTPUT 
done 

Jestem wprawdzie noob bash, ale po wyszukaniu wysokie i niskie, że nie mógł znaleźć właściwego rozwiązania. Najwyraźniej BASH nienawidzi wiodącej białej przestrzeni w ogóle.

Odpowiedz

3

Zamiast:

cat $f | while read line; do 
    echo "$line">>$OUTPUT 
done 

Wykonaj:

cat $f >>$OUTPUT 

(. Jeśli istnieje powód, trzeba zrobić linię rzeczy po linii byłoby dobrze, aby to, że w pytaniu)

+0

To również zabija białe znaki. Przełączyłem się na linię po linii, aby zobaczyć, czy może dałoby mi to więcej opcji na zaoszczędzenie przestrzeni wiodącej. –

+1

nie zabija białej przestrzeni. Dzięki człowiek:> –

+1

Interesujące. Ta odpowiedź została dwukrotnie odrzucona bez żadnego wyjaśnienia. Jeśli masz zamiar przegłosować, powiedz dlaczego. (A jeśli to dlatego, że myślisz "to może być tylko jedno polecenie kota": 1. naprawdę nie, zauważ dodatkowe puste linie wstawione między plikami i 2. Zakładam (być może niepoprawnie), że był to skrypt skrócony dla uproszczenia i wersji rzeczywistej może mieć jakąś dodatkową logikę na jeden plik.) –

3

to zbyt drogi sposób łączenia plików.

cat ../best-practices/*.textile > ../best_practices.textile 

jeśli chcesz dodać pustą (nowej linii) do każdego pliku jak złączyć, wykorzystanie awk

awk 'FNR==1{print "">"out.txt"}{print > "out.txt" }' *.textile 

LUB

awk 'FNR==1{print ""}{print}' file* > out.txt 
+0

Złap mnie o 1 sekundę. –

+1

nice. Dziękuję Ci. Naprawdę bardzo podoba mi się Bash. Teraz po prostu potrzebuję, aby moja wiedza była zgodna z moją miłością. Pozdrawiam –

+0

tytuł moich dokumentów uderza mnie jako ironiczny .. heh –

1

Pozwala to przeplatać nowe linie pomiędzy każdego wejścia plik taki jak w oryginalnym skrypcie:

for f in $FILES; do echo -ne '\n\n' | cat "$f" -; done > $OUTPUT 

Zauważ, że $FILES jest nienotowane, aby działało (w przeciwnym razie dodatkowe nowe linie pojawiają się tylko raz na końcu wszystkich danych wyjściowych), ale $f musi być cytowane, aby chronić spacje w nazwach plików, jeśli istnieją.

40

Jak podkreślali inni, użycie cat lub awk zamiast pętli read-echo jest o wiele lepszym sposobem - unika problemu z przycinaniem białych znaków (i kilkoma innymi, na które się nie natknąłeś) , działa szybciej, a przynajmniej z kotem, jest po prostu czystszy kod. Niemniej jednak, chciałbym zrobić dźgnięcie, aby pętla read-echo działała poprawnie.

Po pierwsze, problem z przycinaniem białych znaków: polecenie odczytu automatycznie przycina wiodące i końcowe białe znaki; można to naprawić, zmieniając definicję białych znaków, ustawiając zmienną IFS na pustą. Ponadto, read zakłada, że ​​odwrotny ukośnik na końcu linii oznacza, że ​​następna linia jest kontynuacją i powinna być połączona z tą linią; aby to naprawić, użyj flagi -r (raw). Trzeci problem polega na tym, że wiele implementacji echa interpretuje sekwencje specjalne w łańcuchu znaków (np. Mogą one zmienić \ n w rzeczywisty znak nowej linii); aby to naprawić, użyj zamiast tego printf. Na koniec, tak jak w przypadku ogólnej zasady higieny skryptów, nie należy używać kota, gdy nie jest to konieczne; zamiast tego użyj przekierowania wejścia. Z tych zmian, wewnętrzna pętla wygląda następująco:

while IFS='' read -r line; do 
    printf "%s\n" "$line">>$OUTPUT 
done <$f 

... istnieją także kilka innych problemów z otaczającej skryptu: linia, która stara się definiować jako pliki z listy dostępnych plików .textile ma cytuje wokół niego, co oznacza, że ​​nigdy nie zostanie rozszerzony na rzeczywistą listę plików.Najlepszym sposobem, aby to zrobić jest użycie tablicy:

FILES=(../best-practices/*.textile) 
... 
for f in "${FILES[@]}" 

(i wszystkie wystąpienia $ F powinna być w cudzysłów w przypadku żadnej z nazw mieć spacji lub innych znaków zabawne w nich - powinny naprawdę zrób to również za pomocą $ OUTPUT, ale skoro jest to zdefiniowane w skrypcie, to naprawdę bezpiecznie odejść.)

Wreszcie, jest echo "">$OUTPUT w górnej części plików pętli, które będą kasować plik wyjściowy co time through (tj. na końcu zawiera tylko ostatni plik tekstowy); to musi być przeniesione przed pętlą. Nie jestem pewien, czy celem było umieszczenie pojedynczej pustej linii na początku pliku, czy trzech pustych linii między plikami (i jednego na początku i dwóch na końcu), więc nie jestem pewien co dokładnie odpowiednim zamiennikiem jest. W każdym razie, oto co mogę się z po zamontowaniu wszystkich tych problemów:

#!/bin/sh 
OUTPUT="../best_practices.textile" 
FILES=(../best-practices/*.textile) 

: >"$OUTPUT" 
for f in "${FILES[@]}" 
do 
    echo "Processing $f file..." 
    echo >>"$OUTPUT" 

    while IFS='' read -r line; do 
    printf "%s\n" "$line">>"$OUTPUT" 
    done <"$f" 

    echo >>"$OUTPUT" 
    echo >>"$OUTPUT" 
done 
+0

Dziękuję za poświęcenie czasu temu Gordonowi, było to bardzo pouczające. –

+0

niesamowite, wyglądało na to wszystko, dzięki –

+0

Najlepsza odpowiedź tutaj! Wielkie dzięki za poświęcenie czasu, aby wyjaśnić tak jasno. :) – Arnlen

0

Prawidłowa odpowiedź, imo jest this, zamieszczonym poniżej:

while IFS= read line; do 
    check=${line:0:1} 
done < file.txt 

Zauważ, że będzie ona dbać o sytuacjach gdzie wejście jest wyprowadzone z innego polecenia, a nie tylko z rzeczywistego pliku.

Należy pamiętać, że można również uprościć przekierowanie, jak pokazano poniżej.

#!/bin/bash 
OUTPUT="../best_practices.textile" 
FILES="../best-practices/*.textile" 
for f in "$FILES" 
do 
    echo "Processing $f file..." 
    { 
    echo 

    while IFS= read line; do 
     echo "$line" 
    done < $f 
    echo 
    echo; 
    } > $OUTPUT 
done 
Powiązane problemy