IO yönlendirmesi ve head komutu


9

.hgignoreBugün Cygwin bash kabuğundan bir dosyayı hızlı bir şekilde düzenlemeye çalışıyordum ve hata olan bir satır ekledim. Bunu yapmak için en iyi yolu olup olmadığından emin değilim, ama hızlı bir şekilde head -1 .hgignore(daha önce sadece dosyada bir satır vardı) rahatsız edici hattı kaldırmak için kullanmayı düşündüm . Elbette, yürütüldüğünde ilk satırı tek çıkış olarak verir.

Ancak çıktıyı yeniden yönlendirmeye ve dosyayı kullanarak yeniden yazmaya çalıştığımda head -1 .hgignore > .hgignore, dosya boştu. Bu neden oluyor? Bunun yerine head -1 .hgignore >> .hgignoreeklemeyi denersem, doğru bir şekilde eklenir, ancak bu istenen sonuç değildir. Kesik yönlendirme bu durumda neden çalışmıyor?


Yanıtlar:


10

Kabuk aşağıdaki gibi bir komut satırı aldığında: command > file.outkabuğun kendisi adlı dosyayı açar (ve belki de oluşturur) file.out. Kabuk, dosya tanımlayıcı 0'ı açıktan aldığı dosya dosya tanımlayıcısına ayarlar. G / Ç yönlendirmesi şu şekilde çalışır: her işlem 0, 1 ve 2 dosya tanımlayıcılarını bilir.

Bunun zor kısmı nasıl açılacağı file.out. Çoğu zaman, file.out0 ofsetinde (yani kesilmiş) yazmak için açılmak istersiniz ve kabuk sizin için budur. .Hgignore'u kısalttı, yazmak için açtı, dosya tanımlayıcıyı 0'a kopyaladı, daha sonra çalıştırdı head. Anında dosya gizleme.

Bash kabuğunda, set noclobberbu davranışı değiştirmek için bir .


İşte görüyorum. Komutu çalıştırmadan önce kabuğun dosyayı kesmiş olduğunu düşündüm, ama nedenini bilmiyordum. Açıklama için teşekkürler!
voithos

10

Bruce'un burada olanları kabuk boru hattıyla cevapladığını düşünüyorum .

Sevdiğim küçük araçlardan biri spongegelen komut moreutils . Hedef çıktı dosyasını açmadan ve verileri yazmadan önce tüm kullanılabilir girdileri "emerek" bu sorunu tam olarak çözer. Boru hatlarını tam olarak nasıl beklediğinizi yazmanıza izin verir:

$ head -1 .hgignore | sponge .hgignore

Zavallı adamın çözümü, çıkışı geçici bir dosyaya bağlamaktır, daha sonra pipline yapıldıktan sonra (örneğin çalıştırdığınız bir sonraki komut) geçici dosyayı orijinal dosya konumuna geri taşımaktır.

$ head -1 .hgingore > .hgignore.tmp
$ mv .hgignore{.tmp,}

Birkaç yıl sonra buna baktığımda bana bir düşünce geldi: sadece yapamaz mıydık head -1 .hgignore | tee .hgignore? teecoreutils içinde ve bir dikmek / yan etkisi olarak, bu da STDOUT yazıyor
voithos

@voithos Bildiğim teekadarıyla, her şey gibi somutlaştırıldığında yazdığı dosyayı açar ve keser, böylece yazma ile kesilmeden önce dosya içeriğini okuma konusundaki yarış koşulunun ana sorununu çözmez.
Caleb

Komutlar şunlardır borulu olduğunu, yani - Aslında, ben farkında değildi bir noktaya getirmek başladı yerine sırayla arasında hemen. Bu doğru mu? Ancak, bunu test ettim ve istenen şeyi yapıyor tee gibi görünüyor . Makinemde sürüm var 8.13.
voithos

1
@voithos Evet bir kanal hattındaki komutlar ve dahil olan tüm giriş / çıkış kanalları ters sırayla başlatılır, böylece boru hattı ilk veri vermeye başladığında veri almaya hazırdır. Muhtemelen çok küçük bir veri yığını kullandığınız ve ihtiyacınız olmadan önce her şeyi bir okuma arabelleğinde önbelleğe aldığınız için testinizin hatalı olduğundan şüpheleniyorum. teeProgram onlara tampon ikiye kurulum değil, dosyalarınızı keser.
Caleb

3

İçinde

head -n 1 file > file

fileönce kesilir head, ancak yazarsanız:

head -n 1 file 1<> file

fileokuma-yazma modunda açıldığı gibi değildir . Bununla birlikte, headyazmayı bitirdiğinde, dosyayı kesmez, bu nedenle yukarıdaki satır bir no-op headolacaktır ( sadece ilk satırı kendi üzerine yeniden yazacak ve diğerlerine dokunmadan bırakacaktır).

Ancak, headdöndükten sonra ve fdhala açıkken, bunu yapan başka bir komutu çağırabilirsiniz truncate.

Örneğin:

{ head -n 1 file; perl -e 'truncate STDOUT, tell STDOUT'; } 1<> file

Burada önemli olan truncateyukarıdaki headfd 1 için imleci ilk satırdan hemen sonra dosyanın içine taşımaktır. İhtiyacımız olmayan ilk satırı yeniden yazıyor, ama bu zararlı değil.

Bir POSIX kafasıyla, ilk satırı yeniden yazmadan gerçekten kurtulabiliriz:

{ head -n 1 > /dev/null
  perl -e 'truncate STDIN, tell STDIN'
} <> file

Burada, headimleç konumunu stdininde hareket ettiren gerçeği kullanıyoruz . İken headtipik performansını artırmak için büyük parçalar alarak girdi okurdum, POSIX için (mümkünse) gerektirecektir seekonun ötesine gitmiş olsaydı daha ilk satırdan sonra geri. Ancak tüm uygulamaların bunu yapmadığını unutmayın.

Alternatif olarak, readbu durumda kabuğun komutunu kullanabilirsiniz :

{ read -r dummy; perl -e 'truncate STDIN, tell STDIN'; } <> file

1
Stephane, yukarıda STDINkullandıklarınıza benzer şekilde kesilebilen standart veya coreutils komutunu biliyor musunuzperl
iruvar

2
@ 1_CR, hayır. olsa da dosyadaki ddherhangi bir keyfi mutlak ofseti kesebilir . Böylece ikinci satırın bayt uzaklığını belirleyebilir ve oradan dd bs=1 seek="$offset" of=file
kısaltabilirsiniz

1

Gerçek İnsanın çözümü

ed .hgignore
$d
wq

veya bir astar olarak

printf '%s\n' '$d' 'wq' | ed .hgignore

Veya GNU sed ile:

sed -i '$d' .hgignore

(Hayır, şaka yapıyorum. İnteraktif bir editör kullanırdım. vi .hgignore GddZZ)


Ben kullanmanın herhangi bir avantajı yoktur, merak ettik :wqüzerinde ZZ?
voithos

Ayrıca, :xparmaklarımın otomatik olarak yaptığı şey budur
glenn jackman

ve ZQaynı:q!
glenn jackman

ZZ ve: x sadece yazacak bir şey varsa yazıyor ...: w dosyaya ihtiyaç duyarsa her zaman diske fsyncs. Sekmeleri kullandığım için xa kullanıyorum.
Temmuz'da xenoterracide

1

Vim'i Ex modunda kullanabilirsiniz:

ex -sc '2,d|x' .hgignore
  1. 2, sonuna kadar satır 2'yi seç

  2. d silmek

  3. x kaydet ve kapat


0

Yerinde dosya düzenleme için, Jürgen Hötzel'in sed 's / c / d /' myFile dosyamdan myFile dosyasına Yönlendirme çıktısında gösterildiği gibi açık dosya tanıtıcı hilesini de kullanabilirsiniz .

exec 3<.hgignore
rm .hgignore  # prevent open file from being truncated
head -1 <&3 > .hgignore

ls -l .hgignore  # note that permissions may have changed

2
Ve rm .hgignoregücünüzün kesilmesinden hemen sonra , saatlerce zor işi alıp götürün. Tamam, önemli değil .hgignore, ama neden yine de karmaşık bir şey yapasın ki? Bu yüzden benim downvote: teknik olarak doğru ama çok kötü bir fikir.
Gilles 'SO- kötü olmayı bırak'

@Gilles, belki de iyi bir fikir değil, ama örneğin perl -i(yerinde düzenleme için) bunu yapar ve bazı uygulamaları da sed -iyaptıysa (GNU'nun en son sürümü sedgörünmüyor olsa da) şaşırmam .
Stéphane Chazelas
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.