Jak zmusić iconv do zastąpienia pliku wejściowego przekonwertowanym wyjściem?

69

Mam skrypt bash, który wylicza każdy plik * .php w katalogu i stosuje się iconvdo niego. Otrzymuje to dane wyjściowe w STDOUT.

Ponieważ dodawanie -oparametru (z mojego doświadczenia) faktycznie zapisuje pusty plik prawdopodobnie przed konwersją, w jaki sposób mogę dostosować skrypt, aby wykonał konwersję, a następnie nadpisał plik wejściowy?

for file in *.php
do
    iconv -f cp1251 -t utf8 "$file"
done
meder omuraliev
źródło
Zobacz także Ostrzeżenie dotyczące „>” .
G-Man,

Odpowiedzi:

76

To nie działa, ponieważ iconvnajpierw tworzy plik wyjściowy (ponieważ plik już istnieje, obcina go), a następnie zaczyna czytać plik wejściowy (który jest teraz pusty). Większość programów zachowuje się w ten sposób.

Utwórz nowy plik tymczasowy dla wyniku, a następnie przenieś go na miejsce.

for file in *.php
do
    iconv -f cp1251 -t utf8 -o "$file.new" "$file" &&
    mv -f "$file.new" "$file"
done

Jeśli iconvnie ma twojej platformy -o, możesz użyć przekierowania powłoki do tego samego efektu.

for file in *.php
do
    iconv -f cp1251 -t utf8 "$file" >"$file.new" &&
    mv -f "$file.new" "$file"
done

spongeNarzędzie Colina Watsona (zawarte w moreutils Joeya Hessa ) automatyzuje to:

for file in *.php
do
    iconv -f cp1251 -t utf8 "$file" | sponge "$file"
done

Ta odpowiedź dotyczy nie tylko iconvdowolnego programu filtrującego. Warto wspomnieć o kilku szczególnych przypadkach:

  • GNU sed i Perl -pmają -iopcję zamiany plików na miejscu.
  • Jeśli plik jest niezwykle duża, Twój filtr jest tylko modyfikację lub usunięcie niektórych części, ale nigdy dodając rzeczy (np grep, tr, sed 's/long input text/shorter text/'), i jak życia niebezpiecznie, może chcesz prawdziwie zmodyfikować plik na swoim miejscu (inne rozwiązania wymienione tutaj utworzyć nowy plik wyjściowy i przenieś go na miejsce, aby oryginalne dane pozostały niezmienione, jeśli polecenie zostanie przerwane z jakiegokolwiek powodu).
Gilles
źródło
3
Nie jestem do końca pewien, czy autorstwo spongenależy przypisać wyłącznie Joeyowi Hessowi; to pakiet, moreutilsktóry zawiera sponge, ale utrzymuje, ale jeśli chodzi o pochodzenie sponge, podążając za linkami ze strony głównej moreutils, znalazłem go pierwotnie opublikowany i zasugerował włączenie Colina Watsona: „Joey pisze o braku nowych narzędzi, które pasuje do filozofii Uniksa. Moim ulubionym z takich rzeczy, które napisałem, jest sponge„(pon., 06 lutego 2006).
imz - Ivan Zakharyaschev
3
Używam Mac OS, nie ma opcji -o w iconv, muszę zmienić `iconv -f cp1251 -t utf8 -o" $ file.new "" $ file "` toiconv -f cp1251 -t utf8 "$file" > "$file.new"
code4j
Niektóre polecenia, na przykład sort, są dość sprytne co do -oparametru, a jeśli wykryją, że plik wyjściowy jest taki sam jak dane wejściowe, wewnętrznie zarządzają plikiem tymczasowym, więc po prostu działa.
jesjimher
56

Alternatywą jest recodeużycie biblioteki libiconv do niektórych konwersji. Jego zachowanie polega na zamianie pliku wejściowego na dane wyjściowe, więc to zadziała:

for file in *.php
do
    recode cp1251..utf8 "$file"
done

Ponieważ recodejako parametr przyjmuje wiele plików wejściowych, możesz oszczędzić forpętlę:

recode cp1251..utf8 *.php
człowiek w pracy
źródło
2
Dzięki, to zasługuje na więcej entuzjastów. Zastanawiam się tylko, gdzie w podręczniku wpatrzono się w 2 kropki między kodowaniami ...
neurino,
2
„WNIOSEK często wygląda jak PRZED… PO, a PRZED i PO są kodowaniami.” W tym podręczniku naprawdę trudno jest przestrzegać wszystkich podwójnych kropek (które są częścią składni) i potrójnych kropek (co oznacza więcej tego). Rada: spróbuj info recodezamiast tego. Jest bardziej gadatliwy.
manatwork
4

Na razie

find . -name '*.php' -exec iconv -f CP1251 -t UTF-8 {} -o {} \;

działa jak marzenie

galeksandrp
źródło
5
Na początku naprawdę myślałem, że to działa. Wygląda jednak na to, że wyjście przekraczające 32 K jest odcięte, a przy jeszcze większej ilości danych powoduje zrzuty rdzeni.
x-yuri
1

Możesz używać Vima w trybie Ex:

ex -sc '%!iconv -f cp1251 -t utf8' -cx "$file"
  1. % wybierz wszystkie linie

  2. ! Uruchom polecenie

  3. x Zapisz i zamknij

Steven Penny
źródło
0

Oto prosty przykład . Powinien dać ci wystarczającą ilość informacji, aby zacząć.

#!/bin/bash
#conversor.sh
#Author.....: dede.exe
#E-mail.....: [email protected]
#Description: Convert all files to a another format
#             It's not a safe way to do it...
#             Just a desperate script to save my life...
#             Use it such a last resort...

to_format="utf8"
file_pattern="*.java"

files=`find . -name "${file_pattern}"`

echo "==================== CONVERTING ===================="

#Try convert all files in the structure
for file_name in ${files}
do
        #Get file format
        file_format=`file $file_name --mime-encoding | cut -d":" -f2 | sed -e 's/ //g'`

        if [ $file_format != $to_format ]; then

                file_tmp="${unit_file}.tmp"

                #Rename the file to a temporary file
                mv $file_name $file_tmp

                #Create a new file with a new format.
                iconv -f $file_format -t $to_format $file_tmp > $file_name

                #Remove the temporary file
                rm $file_tmp

                echo "File Name...: $file_name"
                echo "From Format.: $file_format"
                echo "To Format...: $to_format"
                echo "---------------------------------------------------"

        fi
done;
dede.exe
źródło
0
echo "`iconv -f cp1251 -t utf8 $file`" > "$file"

pracuje dla mnie

CoNsTaR
źródło
0

Możesz użyć find, przynajmniej to działało dla mnie w Raspbian Stretch:

find . -type f -name '*php' -execdir iconv -f cp1251 -t UTF-8 '{}' -o '{}'.tmp \; -execdir mv '{}'.tmp '{}' \;
rannala
źródło
0

Jedną z opcji jest użycie perlinterfejsu iconvi jego -itrybu do edycji w miejscu:

perl -MText::Iconv -i -pe '
  BEGIN{$i=Text::Iconv->new(qw(cp1252 UTF-8));$i->raise_error(1)}
  $_ = $i->convert($_)' ./*.php

Z GNU awkmożesz także zrobić coś takiego:

gawk -v cmd='iconv -f cp1252 -t utf-8' -i inplace '
  {print | cmd}; ENDFILE {close(cmd)}' ./*.php

ksh93Powłoka posiada również >;operatora dla tego, który przechowuje dane wyjściowe w pliku temp która jest zmieniana na przekierowanego pliku jeśli polecenie powiodło się:

for f in *.php; do
  iconv -f cp1252 -t utf-8 < $f >; $f
done
Stéphane Chazelas
źródło