É um UUOC (uso inútil de gato) para redirecionar um arquivo para outro?

36

Se eu quiser fazer o conteúdo file2corresponder ao conteúdo file1, obviamente poderia simplesmente executar cp file1 file2.

No entanto, se eu quero preservar tudo sobre file2 exceto o conteúdo-proprietário, permissões, atributos estendidos, ACLs, hard links, etc., etc., então eu não quero correr cp. * Nesse caso, eu só quero plop o conteúdo de file1em file2.

Parece que o seguinte faria:

< file1 > file2

Mas isso não funciona. file2é truncado para nada e não gravado para. Contudo,

cat < file1 > file2

faz o trabalho.

Surpreendeu-me que a primeira versão não funcione.

A segunda versão é um UUOC? Existe uma maneira de fazer isso sem chamar um comando, apenas usando redirecionamentos?

Nota: Estou ciente de que o UUOC é mais um ponto pedante do que um verdadeiro anti-padrão.

* Como tniles09 descoberto , cp vontade de fato trabalhar neste caso.

Curinga
fonte
3
Se < file1 > file2o que você quer depende do shell.
Michael Homer
13
Bem, isso é um uso inútil de <...
jwodder
2
o que é um anti-padrão ?
mikeserv
6
@ jwodder - isso não é verdade. especialmente quando você está falando de uma cópia. considere o que acontece quando file1não existe ou então é ilegível e você o abre < antes que a > saída seja aberta e depois considere o que acontece quando você permite cattentar abri-lo.
mikeserv
3
@JonathanLeffler No zsh, um comando vazio com redirecionamentos chama cat(por padrão), essencialmente executando o segundo comando. Veja a resposta de Stéphane Chazelas abaixo para saber mais sobre o que cabe em um comentário.
Michael Homer

Respostas:

58

cat < file1 > file2não é um UUOC. Classicamente, <e >faça redirecionamentos que correspondem a duplicações do descritor de arquivo no nível do sistema. As duplicações do descritor de arquivo, por si só, não fazem nada (bem, os >redirecionamentos são abertos com O_TRUNC, para ser preciso, os redirecionamentos de saída truncam o arquivo de saída). Não deixe que os < >símbolos o confundam. Os redirecionamentos não movem dados - eles atribuem descritores de arquivo a outros descritores de arquivo.

Nesse caso, você abre file1e atribui esse descritor de arquivo ao descritor de arquivo 0( <file1== 0<file1) file2e atribui esse descritor de arquivo ao descritor de arquivo 1( >file2== 1>file2).

Agora que você tem dois descritores de arquivo, precisa de um processo para extrair dados entre os dois - e caté para isso.

PSkocik
fonte
11
Talvez seja só eu, mas minha parte favorita desta resposta é o uso da palavra "pá". :) Muito claro, obrigado.
Curinga
1
@Wildcard Eu teria preferido "bombear" a "pá", mas ainda assim uma boa palavra. +1
Mehrdad
por que pá é uma boa palavra?
bubakazouba
1
Uma empurra uma pilha de terra, uma pá cheia de cada vez, de uma pilha para outra, à medida que os dados são copiados buffer por buffer. É uma boa analogia.
BSD
1
Na sua primeira frase, você diz que os descritores de arquivo estão sendo duplicados. Eles estão sendo duplicados ou reatribuídos (como seu segundo parágrafo e o comportamento do recurso parecem indicar)?
Greg Bell
17

Não é porque, como outros já apontaram, o comportamento em questão depende do shell. Como você (o OP) apontou, isso é um pouco pedante , talvez até engraçado? , tipo de tópico.

No entanto, em sistemas GNU, a sua premissa inicial tem outra solução disponível: cp --no-preserve=all file1 file2. Experimente, acho que satisfará a situação descrita (por exemplo, modificar o conteúdo file2sem modificar seus atributos).

Exemplo :

$ ls -l
    total 8
    -rw-r--r-- 1 tniles sambashare 16 Dec 16 12:21 fezzik
    -rw-r--r-- 1 tniles tniles     14 Dec 16 12:16 fred
$ cat *
    Lookout, world!
    Hello, world!
$ cp --no-preserve=all fred fezzik 
$ ls -l
    total 8
    -rw-r--r-- 1 tniles sambashare 14 Dec 16 12:22 fezzik
    -rw-r--r-- 1 tniles tniles     14 Dec 16 12:16 fred
$ cat *
    Hello, world!
    Hello, world!

ATUALIZAÇÃO Na verdade, eu só notei que meu sistema cppor si só parece preservar atributos, a menos que -aou -psão especificados. Estou usando o bash shell e o GNU coreutils. Eu acho que você aprende algo novo todos os dias ...


Resultados do teste (por curinga), incluindo link físico e permissões diferentes:

$ ls -li
total 12
913966 -rw-rw-r-- 1 vagrant vagrant 30 Dec 16 20:26 file1
913965 -rwxrw---- 2 pete    vagrant 39 Dec 16 20:35 file2
913965 -rwxrw---- 2 pete    vagrant 39 Dec 16 20:35 hardlinktofile2
$ cat file1
This is the contents of file1
$ cat file2
This is the original contents of file2
$ cp file1 file2
$ ls -li
total 12
913966 -rw-rw-r-- 1 vagrant vagrant 30 Dec 16 20:26 file1
913965 -rwxrw---- 2 pete    vagrant 30 Dec 16 20:37 file2
913965 -rwxrw---- 2 pete    vagrant 30 Dec 16 20:37 hardlinktofile2
$ cat file1
This is the contents of file1
$ cat file2
This is the contents of file1
$ 
tniles
fonte
Agradável. Fiz meu próprio teste, incluindo um link físico e permissões diferentes, e parece que você está correto.
Curinga
Adicionado meus resultados de teste; Espero que você não se importe. :) Eu não testei ACLs ou atributos estendidos, mas como o número de inode é preservado, tenho 99% de certeza de que esses também seriam.
Curinga
Bom ... não se importe. :-)
tniles
13

No zshshell onde < file1 > file2funciona, o shell invoca cat.

Para uma linha de comando que consiste apenas em redirecionamentos e nenhum comando ou designação, zshchama $NULLCMD( catpor padrão), a menos que o único redirecionamento seja <aquele em que caso $READNULLCMD( pagerpor padrão) é chamado. (a menos que zshesteja dentro shou cshemulação, caso em que se comporta como as conchas que emula).

Tão:

< file1 > file2

é realmente o mesmo que

cat < file1 > file2

e

< file1

é o mesmo que

pager < file1
Stéphane Chazelas
fonte
Para que
conste
8
< from > to

não funciona porque não há comando lá; nenhum processo. O shell abre / cria os arquivos e organiza os redirecionamentos (o que significa que os descritores de arquivos que referenciam esses arquivos são plantados como 0 e 1: entrada padrão e saída padrão). Mas não há nada para executar um loop para ler da entrada padrão e gravar na saída padrão.

zshfaz esse trabalho substituindo um comando configurável pelo usuário neste caso "comando nulo". O comando não está visível na linha de comando, mas ainda está lá. Um processo é criado para ele e funciona da mesma maneira. NULLCMDé cat, por padrão, de modo que < from > torealmente significa cat < from > to em zsh, a menos que NULLCMDestá definido para algo mais; é um comando "gato implícito".

Um "uso inútil de gato" ocorre quando caté usado como intermediário para ler um arquivo e alimentar os dados para outro processo, cujo descritor de arquivo pode ser conectado apenas ao arquivo original.

Se catfor removível da situação, de modo que os comandos restantes ainda possam executar a mesma tarefa, será inútil. Se não for removível, não será inútil.

# useless, removable:
$ cat archive.tar | tar tf -    #  -->  tar tf archive.tar

# not removable (in POSIX shell):
$ cat > file
abc
[Ctrl-D]

# likewise:
STRING=$(cat file)

Um catque é substituível não é a mesma coisa. Por exemplo, em vez de cat > filepodemos usar vi filepara criar o arquivo. Isso não conta como remoção cat, ao usar o que resta para realizar a mesma tarefa.

Se caté o único comando no pipeline, é claro que não pode ser removido; nenhum rearranjo do que sobrar fará o trabalho equivalente.

Alguns scripts de shell usam catporque acham que permite mover o operando de entrada para mais perto do lado esquerdo da linha de comando. No entanto, os redirecionamentos podem estar em qualquer lugar na linha de comando:

# If you're so inclined:
# move source archive operand to the left without cat:
$ < archive.tar tar xf - > listing
Kaz
fonte
Aliás, você não precisa usar o f -tar. tar xf -é justo tar x.
dnt
@mikeserv Onde diz que catestá envolvido na criação do arquivo? A resposta diz claramente que o shell faz isso. A que problema > filevocê está se referindo? Costumo usá-lo sozinho para truncar um arquivo existente com tamanho zero ou garantir que ele exista. Esta pergunta é sobre por < from > toque não funciona como cat < from > to, e UUoC, não "por favor me dê razões pelas quais catnão é um bom substituto para cp".
Kaz
1
@dnt, taré o arquivador de fitas . Muitas tarimplementações ainda funcionam com o primeiro dispositivo de fita por padrão.
Stéphane Chazelas
1

< file1 > file2 Parece ser dependente do shell, no zsh funciona, no bash não.

edit: declaração falsa excluída

tastytea
fonte
cp -apreserva os atributos do arquivo1 e substitui os atributos do arquivo2. Oposto ao comportamento desejado. Além disso, não posso dizer, mesmo olhando para a página de manual, o que acontecerá com os links físicos, mas acho seguro dizer que os links físicos do arquivo2 não serão preservados.
Curinga
Você está certo, eu não li a pergunta com cuidado suficiente.
Tasty16 16/12
1

Além de todas as boas respostas, você pode evitar um UUOC simulando um cat:

awk 1 file1 > file2   # For line-oriented text, not binaries.
dd if=file1 of=file2  # Works for binary files, too.
# many more geeky ways.

Esses comandos não copiam os metadados do arquivo, como cpseria simples .

Jens
fonte
É verdade, mas vale a pena mencionar que eles não têm vantagens e apenas desvantagens (desempenho, confiabilidade) cat. Aqui você precisa de um comando para empurrar dados entre os dois descritores de arquivo e caté um dos melhores para isso. Veja também o pvque seria capaz de usar splice()no Linux por quinos (embora não seja fadvise(POSIX_FADV_SEQUENTIAL)como o GNU cat).
Stéphane Chazelas
O ddcomando para arquivos binários parece bom ... ou catfuncionaria tão bem quanto para arquivos binários?
Curinga
O @Wildcard cattambém funciona para arquivos binários (o Unix geralmente não distingue; no entanto, algumas ferramentas trabalham especificamente linha por linha, como awk, grep, wc, ... uma ferramenta orientada a linhas pode recusar-se a lidar com linhas excessivamente grandes.)
Jens
2
@ StéphaneChazelas Esta resposta também foi planejada como uma brincadeira. Parece que, apesar da temporada, algumas pessoas são alérgicas à diversão (não direcionadas a você; eu valorizo ​​sua experiência em shell e os padrões do Opengroup funcionam).
Jens
sed '' < file1 > file2;-)
Trauma digital
0

Se funcionar, não conserte.

eu usaria

cat < file1 > file2

e não suar o PC da semântica.

krazykyngekorny
fonte