É seguro redirecionar stdout e stderr para o mesmo arquivo sem cópias do descritor de arquivo?

27

Eu começo no diretório vazio.

$ touch aFile
$ ls
aFile

Então, eu tenho lsdois argumentos, um dos quais não está neste diretório. Eu redireciono os dois fluxos de saída para um arquivo chamado output. Eu uso >>para evitar escrever simultaneamente.

$ ls aFile not_exist >>output 2>>output
$ cat output
ls: cannot access 'not_exist': No such file or directory
aFile

O que parece funcionar. Existem perigos para essa abordagem?

exit_status
fonte
6
Essa foi uma votação rápida. Demorou cinco segundos. Você pode me dizer como pode avaliar o valor da minha pergunta tão rapidamente? E melhor ainda, o que há de errado com isso para que eu possa melhorá-lo?
exit_status
Por que você não usa o mais padrão ls aFile not_exist &>>outputaqui? (Observe, eu estou supondo que você esteja usando o bash .)
FedonKadifeli
5
Porque isso não me ajuda a entender o que estou perguntando. Eu sei como redirecionar esses fluxos para o mesmo arquivo, mesmo que seja portável. O que eu quero saber é se há algo errado com o que sugeri na pergunta. @FedonKadifeli
exit_status
11
@FedonKadifeli &>>NÃO é padrão. É uma sintaxe depreciada e ambígua que funciona de maneira diferente em diferentes tipos de conchas. Eu me pergunto de onde vocês tiram suas coisas.
Tio Billy
4
Bash não é um padrão . O padrão POSIX exige que ls &>>foo ...sejam analisados ​​como dois comandos ls &e >>foo ..., e é dessa maneira que outros shells como o /bin/shdo Ubuntu o estão analisando. Por ter sido preterido, você pode olhar aqui - embora eu não finja que é algum tipo de autoridade. Você pode perguntar aos bashmantenedores se eles consideram usar essa uma boa idéia.
Tio Billy

Respostas:

22

Não, não é tão seguro quanto o padrão >>bar 2>&1.

Quando você está escrevendo

foo >>bar 2>>bar

você está abrindo o bararquivo duas vezes O_APPEND, criando dois objetos de arquivo completamente independentes [1], cada um com seu próprio estado (ponteiro, modos abertos, etc.).

Isso é muito diferente do 2>&1que está apenas chamando a dup(2)chamada de sistema e cria os aliases intercambiáveis ​​stderr e stdout para o mesmo objeto de arquivo.

Agora, há um problema com isso:

O_APPENDpode levar a arquivos corrompidos nos sistemas de arquivos NFS se mais de um processo anexar dados a um arquivo de uma só vez. Isso ocorre porque o NFS não suporta anexar a um arquivo, portanto o kernel do cliente precisa simulá-lo, o que não pode ser feito sem uma condição de corrida.

Você geralmente pode contar com a probabilidade do arquivo como barem foo >>bar 2>&1sendo gravados ao mesmo tempo em dois lugares separados sendo bastante baixa. Mas, pelo seu, >>bar 2>>barvocê apenas o aumentou em uma dúzia de ordens de magnitude, sem qualquer motivo.

[1] "Abrir descrições de arquivo" na linguagem POSIX.

mosvy
fonte
3
Formalmente, para arquivos no modo anexado, é seguro . A questão citada é um bug no NFS que o torna inadequado (não compatível com POSIX) como um sistema de arquivos. No caso do modo não anexado, porém, nunca é seguro.
R ..
11
Isso é imaterial. O apêndice duplo do OP não é seguro (além de ser completamente inútil). E O_APPENDé uma espécie de falha de qualquer maneira - bastante oneroso para implementar corretamente.
mosvy
Acredito que a condição de corrida do NFS é apenas entre clientes diferentes. O sistema operacional do cliente deve coordenar todas as gravações entre seus processos.
Barmar
@ Barmar, isso seria verdade se o sistema operacional do cliente se preocupasse apenas com sua própria exibição de um arquivo nfs. Porém, ao gravar no arquivo nfs aberto com O_APPEND, o cliente recuperará primeiro o tamanho "real" do arquivo do servidor ("revalidará" o inode) e, em seguida, fará a atualização de inode em cache + write + em cache, e somente a última parte será feito sob bloqueios, o que significa que a primeira parte ainda pode recuperar um tamanho obsoleto do servidor e substituir o correto do inode local / em cache. Mesmo problema com lseek(SEEK_END).
mosvy
Ainda não vejo como isso poderia causar condições de corrida entre dois fluxos no mesmo cliente. Ambos os fluxos devem se referir ao mesmo inode em cache local.
Barmar
22

O que acontece quando você faz

some_command >>file 2>>file

é que fileserá aberto para anexar duas vezes. Isso é seguro em um sistema de arquivos POSIX. Qualquer gravação que aconteça no arquivo quando ele for aberto para anexar ocorrerá no final do arquivo, independentemente de os dados chegarem ao fluxo de saída padrão ou ao fluxo de erro padrão.

Isso depende do suporte para operações de gravação de acréscimo atômico no sistema de arquivos subjacente. Alguns sistemas de arquivos, como o NFS, não suportam anexos atômicos. Veja, por exemplo, a pergunta "O arquivo é anexado atômico no UNIX?" No StackOverflow.

Usando

some_command >>file 2>&1

funcionaria mesmo no NFS.

No entanto, usando

some_command >file 2>file

não é seguro, pois o shell truncará o arquivo de saída (duas vezes) e qualquer gravação que ocorrer em um fluxo substituirá os dados já gravados pelo outro fluxo.

Exemplo:

$ { echo hello; echo abc >&2; } >file 2>file
$ cat file
abc
o

A hellosequência é gravada primeiro (com uma nova linha final) e, em seguida, a sequência abcseguida por uma nova linha é gravada a partir do erro padrão, substituindo a hell. O resultado é a string abccom uma nova linha, seguida pelo que resta da primeira echosaída, uma oe uma nova linha.

Trocar os dois echoao redor do ferimento produz apenas hellono arquivo de saída, pois a sequência é escrita por último e é maior que a abcsequência. A ordem na qual os redirecionamentos ocorrem não importa.

Seria melhor e mais seguro usar o método mais idiomático

some_command >file 2>&1
Kusalananda
fonte
11
Embora isso >>aconteça com as conchas modernas, esse não era o caso no shell Bourne ou Thomson (de onde vem), onde >>se abriria para escrever e procuraria até o fim (suponho que O_APPEND ainda não foi inventado naquela época). Mesmo no Solaris 10, /bin/sh -c '(echo a; echo b >&2) >> file 2>> file; cat file'saídas b.
Stéphane Chazelas
@ StéphaneChazelas Isso é um problema com a implementação shou o sistema de arquivos do Solaris 10 ?
Kusalananda
11
Era o que >>originalmente estava fazendo, não estava abrindo com O_APPEND, estava abrindo sem e procurando até o fim. Não é tanto um problema, é o que ele estava fazendo e foi documentado para fazer.
Stéphane Chazelas
0

Depende do que você deseja alcançar. Cabe a você decidir se há erros no mesmo arquivo que a saída. Isso é apenas salvar o texto em um arquivo com a funcionalidade do shell, que permite redirecionar conforme desejado. Não existe um sim ou não absoluto. Como tudo no Linux, isso pode ser feito de várias maneiras, este é o meu caminho ls notExistingFile existingFile >> output 2>&1 Para responder à pergunta: Em termos de redirecionamento, sim, é perfeitamente seguro.

Anjo
fonte
Há mais do que você está dizendo aqui. O mesmo exercício com em >vez de >>substituirá alguns caracteres. Portanto, não é apenas o shell que me permite redirecionar, porque quando eu redireciono com >, o resultado é diferente. Então, existem nuances com >, existem com >>?
exit_status
Sim, será diferente. Como eu disse, depende do seu objetivo >- substituir. >>- append
Angel