Posso fazer com que 'cut` mude um arquivo no lugar?

17

A manpágina não me dá muita esperança, mas espero que seja um recurso não documentado (e / ou específico do GNU).

Hank Gay
fonte

Respostas:

14

Você não pode. Use ed ou GNU sed ou perl, ou faça o que eles fazem nos bastidores, que é criar um novo arquivo para o conteúdo.

edportátil:

ed foo <<EOF
1,$s/^\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/
w
q
EOF

GNU sed:

sed -i -e 's/^\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/' foo

Perl:

perl -i -l -F, -pae 'print @F[1,3]' foo

cut, criando um novo arquivo (recomendado, porque se o seu script for interrompido, você poderá executá-lo novamente):

cut -d , -f 1,3 <foo >foo.new &&
mv -f foo.new foo

cut, substituindo o arquivo no lugar (mantém a propriedade e as permissões de foo, mas precisa de proteção contra interrupções):

cp -f foo foo.old &&
cut -d , -f 1,3 <foo.old >foo &&
rm foo.old

Eu recomendo usar um dos cutmétodos baseados em. Dessa forma, você não depende de nenhuma ferramenta não padrão, pode usar a melhor ferramenta para o trabalho e controlar o comportamento em interrupção.

Gilles 'SO- parar de ser mau'
fonte
Melhor do que .oldmétodo para mudanças no local,echo "$(cut -d , -f 1,3 <foo)" > foo
GypsyCosmonaut
11
@GypsyCosmonaut Não, isso não é "melhor". É mais frágil. Seu único benefício é que é mais curto para digitar. O principal problema do seu método é que, se ocorrer um erro ao processar o arquivo de entrada ou gravar a saída, os dados serão perdidos. Também não funciona com dados binários: a saída será truncada no primeiro byte nulo. Mesmo com arquivos de texto, ele remove linhas vazias do final do arquivo. Em arquivos grandes, isso pode falhar porque os dados precisam ser armazenados como uma string na memória do shell (e lembre-se, se isso acontecer, os dados serão perdidos).
Gilles 'SO- stop be evil'
oO Obrigado, eu não sabia que poderia haver problemas com byte nulo #
GypsyCosmonaut
Eu acho que isso é mais simples:cut -d , -f 1,3 foo > foo.new rm foo mv foo.new foo
LoMaPh
@LoMaPh De fato, não sei por que renomeei o arquivo antigo: ele não tem vantagens em renomear o novo arquivo. Também é mais simples, porque você não precisa da etapa rm foo. E você não deve ligar rm foo, porque mv foo.new fooé atômico: remove a versão antiga e coloca a nova versão ao mesmo tempo.
Gilles 'SO- stop be evil'
23

O pacote moreutils do ubuntu (e também do debian ) tem um programa chamado sponge, que também resolve o seu problema.

Da esponja do homem:

esponja lê a entrada padrão e a grava no arquivo especificado. Ao contrário de um redirecionamento de shell, a esponja absorve toda a sua entrada antes de abrir o arquivo de saída. Isso permite que os pipelines restritos sejam lidos e gravados no mesmo arquivo.

O que permitiria fazer algo como:

cut -d <delim> -f <fields> somefile | sponge somefile
Kjetil Jorgensen
fonte
9

Eu não acho que isso é possível usando cutsozinho. Não consegui encontrá-lo na página de informações ou homem. Você pode fazer algo como

mytemp=$(mktemp) && cut -d" " -f1 file > $mytemp && mv $mytemp file

mktempfaz de você um arquivo temporário relativamente seguro no qual você pode canalizar a cutsaída.

Steven D
fonte
1

Experimente o vim-way:

$ ex -s +'%!cut -c 1-10' -cxa file.txt

Isso editará o arquivo no local (faça o backup primeiro).

Como alternativa grep, use sedou gawk.

kenorb
fonte
0

Você pode usar o slurp com o POSIX Awk:

cut -b1 file | awk 'BEGIN{RS="";getline<"-";print>ARGV[1]}' file

Exemplo

Steven Penny
fonte
0

Bem, como cutproduz menos saída do que lê, você pode:

cut -c1 < file 1<> file

Ou seja, torne o stdin fileaberto no modo somente leitura e o stdout fileaberto no modo leitura + gravação sem truncamento ( <>).

Dessa forma, cutapenas substituirá o arquivo por si próprio. No entanto, ele deixará o restante do arquivo intocado. Por exemplo, se filecontém:

foo
bar

A saída se tornará:

f
b
bar

Eles f\nb\nforam substituídos foo\n, mas barainda estão lá. Você precisaria truncar o arquivo após a cutconclusão.

Com ksh93, você pode fazê-lo com seu <>;operador, que age como <>exceto, se o comando for bem-sucedido, ftruncate()é chamado no descritor de arquivo. Então:

cut -c1 < file 1<>; file

Com outras conchas, você precisaria fazer isso ftruncate()através de outros meios, como:

{ cut -c1 < file; perl -e 'truncate STDOUT, tell STDOUT';} 1<> file

embora invocar perlapenas para isso seja um pouco exagerado aqui, especialmente considerando que perlpode facilmente fazer esse cuttrabalho, como:

perl -pi -e '$_ = substr($_, 0, 1)' file

Lembre-se de que, com todos os métodos que envolvem a reescrita no local real, se a operação for interrompida no meio do caminho, você acabará com um arquivo corrompido. O uso de um segundo arquivo temporário evita esse problema.

Stéphane Chazelas
fonte