Bash kullanarak dosyasını (argüman) "yerinde" düzenleyen herhangi bir komutu nasıl çalıştırırım?


110

sortBash komutuyla sıralamak istediğim bir temp.txt dosyam var .

Sıralanan sonuçların orijinal dosyanın yerini almasını istiyorum.

Örneğin bu işe yaramıyor (boş bir dosya alıyorum):

sortx temp.txt > temp.txt

Bu, geçici dosyalara kopyalamaya başvurmadan tek satırda yapılabilir mi?


DÜZENLEME: -oSeçenek için çok güzel sort. sortSorumda örnek olarak kullandım . Diğer komutlarla aynı problemle karşılaşıyorum:

uniq temp.txt > temp.txt.

Daha iyi bir genel çözüm var mı?


Yanıtlar:


171
sort temp.txt -o temp.txt

3
Bu bir cevaptır. Aslında bu soruna genel bir çözüm olup olmadığını merak ediyordum. Örneğin, bir dosyadaki tüm UNIQ satırlarını "yerinde" bulmak istersem, -o
jm

Genel değildir, ancak benzersiz satırları bulmak için -u ile GNU sıralaması kullanabilirsiniz
James

Mesela izin vermek için sorunu çözen var sort --inplace *.txtmı? Bu çılgınca havalı olurdu
sehe

@sehe Şunu dene:find . -name \*.txt -exec sort {} -o {} \;
Keith Gaughan

29

A'nın sortçıktı almaya başlamadan önce tüm girdileri görmesi gerekir. Bu nedenle, sortprogram kolayca bir dosyayı yerinde değiştirme seçeneği sunabilir:

sort temp.txt -o temp.txt

Özellikle, GNU'nun dokümantasyonu şunusort söylüyor:

Normalde sort, çıktı dosyasını açmadan önce tüm girdileri okur, böylece sort -o F Fve gibi komutları kullanarak bir dosyayı güvenli bir şekilde yerinde sıralayabilirsiniz cat F | sort -o F. Bununla birlikte, sortwith --merge( -m) tüm girdileri okumadan önce çıktı dosyasını açabilir, bu nedenle cat F | sort -m -o F - Gsıralama yazmaya Fbaşlamadan önce yazmaya başlayabileceği için gibi bir komut güvenli değildir cat.

BSD'nin dokümantasyonu sortşöyle diyor:

Eğer [] çıktı dosyası girdi dosyalarından biriyse, sıralama, çıktıyı [çıktı] dosyasına sıralayıp yazmadan önce geçici bir dosyaya kopyalar.

Gibi komutlar uniq, girdiyi okumayı bitirmeden çıktı yazmaya başlayabilir. Bu komutlar genellikle yerinde düzenlemeyi desteklemez (ve bu özelliği desteklemeleri daha zor olur).

Genellikle geçici bir dosyayla bu sorunu çözersiniz veya bir ara dosyadan kesinlikle kaçınmak istiyorsanız, yazmadan önce tüm sonucu saklamak için bir arabellek kullanabilirsiniz. Örneğin perl:

uniq temp.txt | perl -e 'undef $/; $_ = <>; open(OUT,">temp.txt"); print OUT;'

Burada, perl bölümü uniqdeğişkendeki tüm çıktıyı okur $_ve ardından bu verileri orijinal dosyanın üzerine yazar. Aynı şeyi seçtiğiniz betik dilinde de yapabilirsiniz, hatta Bash'de bile. Ancak, tüm dosyayı depolamak için yeterli belleğe ihtiyaç duyacağını unutmayın; bu, büyük dosyalarla çalışırken tavsiye edilmez.


19

İşte daha genel bir yaklaşım, uniq, sort ve whatnot ile çalışıyor.

{ rm file && uniq > file; } < file

14
İle bir başka jenerik yaklaşım, spongemoreutils gelen: cat file |frobnicate |sponge file.
Tobu

3
@Tobu: Neden bunu ayrı bir cevap olarak göndermiyorsunuz?
Flimm

1
Muhtemelen bunun dosya izinlerini korumadığını unutmamak gerekir. Umask'iniz yeni izinlerin ne olacağını belirler.
wor

1
Zor biri. Tam olarak nasıl çalıştığını açıklayabilir misin?
patryk.beza

2
@ patryk.beza: Sırayla: FD girişi orijinal dosyadan açılır; orijinal telefon rehberi girişi silinir; yeniden yönlendirme işlenir ve eskisinin sahip olduğu aynı ada sahip yeni bir boş dosya oluşturulur; ardından komut çalışır.
Charles Duffy

10

Tobu'nun sünger hakkındaki yorumu, kendi başına bir cevap olmayı garanti ediyor.

Moreutils ana sayfasından alıntı yapmak için :

Muhtemelen şu ana kadar daha fazla kullanımda en genel amaçlı araç süngerdir (1), bu da şu gibi şeyler yapmanızı sağlar:

% sed "s/root/toor/" /etc/passwd | grep -v joey | sponge /etc/passwd

Ancak, Steve Jessop'un burada yorumladığısponge aynı sorundan muzdariptir . Ardışık düzendeki komutlardan herhangi biri spongebaşarısız olmadan önce , orijinal dosyanın üzerine yazılacaktır.

$ mistyped_command my-important-file | sponge my-important-file
mistyped-command: command not found

Uh-oh, my-important-filegitti.


1
Sünger, girdi dosyasını değiştirmek için kullanılacağını bilir ve başlangıçta bir yarış durumundan kaçınmak için geçici bir dosya oluşturur. Bunun çalışması için, süngerin boru hattındaki son öğe olması ve çıkış dosyasının kendisinin (örneğin, kabuk seviyesinde çıktı yeniden yönlendirmesinin aksine) oluşturulmasına izin verilmesi gerekir. BTW: 'Başarısız' durumu için kolay bir kaynak kodu düzeltmesi gibi görünüyor, bir boru hatası durumunda geçici dosyayı yeniden adlandırmamak (süngerin neden bu seçeneğe sahip olmadığını bilmiyorum).
Brent Bradburn

set -o pipefailBetiğinizin başına eklerseniz , hata mistyped_command my-important-filedurumunda betiğin çalıştırılmadan hemen önce çıkmasına neden olur sponge, böylece önemli dosya korunur.
Elouan Keryell - Hatta

6

İşte başlıyorsun, bir satır:

sort temp.txt > temp.txt.sort && mv temp.txt.sort temp.txt

Teknik olarak geçici bir dosyaya kopyalama yoktur ve 'mv' komutu anında olmalıdır.


6
Hm. Yine de geçici bir dosya temp.txt.sort çağırırım.
JesperE

5
Bu kod risklidir, çünkü sıralama herhangi bir nedenle işini tamamlamadan başarısız olursa, orijinalin üzerine yazılır.
Steve Jessop

1
Disk alanı eksikliği makul bir neden veya bir sinyaldir (kullanıcı CTRL-C'ye vurur).
Steve Jessop

5
bunun gibi bir şey kullanmak istiyorsanız; yerine && (mantıksal ve) kullanın; çünkü bunu kullanmak, bir komutun başarısız olması durumunda bir sonraki komutun çalıştırılmamasını sağlayacaktır. örneğin: cp backup.tar /root/backup.tar && rm backup.tar kopyalama haklarınız yoksa güvende olacaksınız çünkü dosya silinmeyecektir
daniels

1
önerilerinizi dikkate almak için cevabımı değiştirdim, teşekkürler
davr

4

sort file -o fileCevabı beğendim ama aynı dosya adını iki kez yazmak istemiyorum.

BASH geçmişi genişletmeyi kullanma :

$ sort file -o !#^

tuşuna bastığınızda geçerli satırın ilk argümanını alır enter.

Yerinde benzersiz bir sıralama:

$ sort -u -o file !#$

geçerli satırdaki son argümanı yakalar.


3

Birçoğu -o seçeneğinden bahsetmiştir . İşte man sayfası kısmı.

Man sayfasından:

   -o output-file
          Write output to output-file instead of to the  standard  output.
          If  output-file  is  one of the input files, sort copies it to a
          temporary file before sorting and writing the output to  output-
          file.

3

Bu, büyük ölçüde bellek kısıtlamalı olabilir, ancak ara verileri bellekte saklamak için awk kullanabilir ve sonra geri yazabilirsiniz.

uniq temp.txt | awk '{line[i++] = $0}END{for(j=0;j<i;j++){print line[j]}}' > temp.txt

Bence bu mümkün> (komutundan önce dosya kesikler uniqbu durumda) okur.
Martin

3

spongeDaha yaygın olana bir alternatif sed:

sed -ni r<(command file) file

Herhangi komuta (çalışır sort, uniq, tac, ...) ve kullanımları çok iyi bilinen sed'ın -iseçeneği (yerinde düzenleme dosyaları).

Uyarı:command file Önce deneyin çünkü dosyaları yerinde düzenlemek doğası gereği güvenli değildir.


açıklama

İlk olarak, sed(orijinal) satırları ( -nseçenek ) yazdırmamayı söylüyorsunuz ve sed's rkomutunun ve bash' s Process Substitution'ın<(command file) yardımıyla, tarafından oluşturulan içerik yerinde kaydedilen çıktı olacaktır .


İşleri daha da kolaylaştırmak

Bu çözümü bir işleve sarabilirsiniz:

ip_cmd() { # in place command
    CMD=${1:?You must specify a command}
    FILE=${2:?You must specify a file}
    sed -ni r<("$CMD" "$FILE") "$FILE"
}

Misal

$ cat file
d
b
c
b
a

$ ip_cmd sort file
$ cat file
a
b
b
c
d

$ ip_cmd uniq file
$ cat file
a
b
c
d

$ ip_cmd tac file
$ cat file
d
c
b
a

$ ip_cmd
bash: 1: You must specify a command
$ ip_cmd uniq
bash: 2: You must specify a file


1

uniqYeteneği eklemek için , olumsuz yönleri nelerdir:

sort inputfile | uniq | sort -o inputfile


0

sortProgramı kullanmakta ısrar ediyorsanız , bir ara dosya kullanmanız gerekir sort- bellekte sıralama seçeneği olduğunu sanmıyorum . Sıralama stdin için arabellek boyutunun tüm dosyaya sığacak kadar büyük olduğunu garanti edemezseniz, stdin / stdout ile herhangi bir başka numara başarısız olacaktır.

Düzenleme: utan bana. sort temp.txt -o temp.txtmükemmel çalışıyor.


Q'yu "yerinde" olarak da okudum ama ikinci okuma beni gerçekten istemediğine
inandırdı

0

Başka bir çözüm:

uniq file 1<> file

O olsa Bu unutulmamalıdır <>çünkü hile sadece bu durumda çalışır uniqözel olduğunu o yolda bazı bırakarak çıkış hatlarına sadece kopya girdi hatları,. Diğer komut (örneğin varsa sed) girişini değiştirecek olan kullanıldı (örn her değiştirecek aINTO aasonra geçersiz kılabilirsiniz,) file(giriş yeterince büyük olduğunu a daha sağlayarak, bir anlam ve sonsuz hatta döngü yapmazlar yollarla tek okuma tamponu).
David
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.