Em um script de shell ...
Como capturar stdin em uma variável sem remover nenhuma nova linha à direita?
Agora eu tentei:
var=`cat`
var=`tee`
var=$(tee)
Em todos os casos $var
, não haverá a nova linha à direita do fluxo de entrada. Obrigado.
TAMBÉM: Se não houver uma nova linha à direita na entrada, a solução não deve adicionar uma .
ATUALIZAÇÃO À LUZ DA RESPOSTA ACEITADA:
A solução final que usei no meu código é a seguinte:
function filter() {
#do lots of sed operations
#see https://github.com/gistya/expandr for full code
}
GIT_INPUT=`cat; echo x`
FILTERED_OUTPUT=$(printf '%s' "$GIT_INPUT" | filter)
FILTERED_OUTPUT=${FILTERED_OUTPUT%x}
printf '%s' "$FILTERED_OUTPUT"
Se você deseja ver o código completo, consulte a página do github para expandr , um pequeno script de shell de filtro de expansão de palavra-chave git de código aberto que desenvolvi para fins de segurança da informação. De acordo com as regras configuradas nos arquivos .gitattributes (que podem ser específicos da ramificação) e no git config , o git canaliza cada arquivo através do script shell expandr.sh sempre que o fizer dentro ou fora do repositório. (É por isso que era fundamental preservar as novas linhas finais, ou a falta delas). Isso permite limpar informações confidenciais e trocar diferentes conjuntos de valores específicos do ambiente para teste, preparação e ramificações ativas.
filter
levastdin
- corresed
. Você pegastdin
em$GIT_INPUT
seguida, imprimir que volta aostdout
longo de um tubo parafilter
e pegar o seustdout
no$FILTERED_OUTPUT
e, em seguida, imprimi-lo de volta parastdout
. Todos os 4 linhas na parte inferior do seu exemplo acima pode ser substituído com apenas este:filter
. Sem querer ofender aqui, é só ... você está trabalhando demais. Você não precisa das variáveis do shell na maioria das vezes - basta direcionar a entrada para o lugar certo e repassá-la.filter
, ele adicionará caracteres de nova linha ao final de qualquer fluxo de entrada que não tenha terminado em nova linha inicialmente. De fato, originalmente fiz isso,filter
mas encontrei o problema que me levou a essa solução, porque nem "sempre adiciona novas linhas" nem "sempre tira novas linhas" são soluções aceitáveis.sed
provavelmente fará a nova linha extra - mas você deve lidar com issofilter
não com todo o resto. E todas essas funções que você tem basicamente fazem a mesma coisa - ased s///
. Você está usando o shell para canalizar os dados salvos em sua memória, parased
quesed
possam substituí- los por outros dados que o shell armazenou na memória parased
canalizá-los de volta para o shell. Por que não apenas[ "$var" = "$condition" ] && var=new_value
? Também não recebo as matrizes - você está armazenando o nome da matriz e[0]
depoissed
substituindo-a pelo valor[1]
? Talvez conversar?filter
? Funciona perfeitamente como está. A respeito de como o código no meu link funciona e por que eu o configurei da maneira que fiz, vamos falar sobre isso em uma sala de bate-papo.Respostas:
As novas linhas à direita são removidas antes que o valor seja armazenado na variável Você pode querer fazer algo como:
e use em
${var%x}
vez de$var
. Por exemplo:Observe que isso resolve o problema de novas linhas à direita, mas não o byte nulo (se a entrada padrão não for texto), pois de acordo com a substituição do comando POSIX :
Mas as implementações de shell podem preservar bytes nulos.
fonte
Você pode usar o
read
built-in para fazer isso:Para um script ler STDIN, seria simplesmente:
Não sei ao certo em que conchas isso funcionará. Mas funciona bem no bash e no zsh.
fonte
-d
a substituição do processo (<(...)
) é portátil; esse código não funcionarádash
, por exemplo.-d
, é por isso que coloco o aviso no final. O OP não especifica o shell.dash
. Você apenas usa<<HEREDOC\n$(gen input)\nHEREDOC\n
- indash
- que usa tubos para heredocs da mesma forma que outros reservatórios os utilizam para substituição de processos - não faz diferença. Aread -d
coisa é apenas especificar um delimitador - você pode fazer o mesmo de uma dúzia de maneiras - apenas tenha certeza. Embora você precise de um pouco de atençãogen input
.IFS=''
provavelmente não é necessário. É feito para queread
não colapsem espaços. Mas quando está lendo uma única variável, não tem efeito (que eu me lembre). Mas eu me sinto mais seguro deixá-la em :-)Você pode fazer como:
O que quer que você faça
$var
desaparece assim que você sai desse{ current shell ; }
grupo de qualquer maneira. Mas também pode funcionar como:fonte
$var
no{ ... }
agrupamento. Por exemplo, se este comando for executado dentro de um loop e for necessário$var
fora do loop.{}
chaves. É verdade - e é explicitamente observado na resposta - que o valor de$var
é muito provável que desapareça completamente quando o{ current shell; }
agrupamento é fechadas. Existe alguma maneira mais explícita de dizer isso do que o que você faz$var
desaparece ...?input | sed "s/'"'/&"&"&/g;s/.*/process2 '"'-> &'/" | sh
_variables
função debash_completion
, que armazena o resultado de uma substituição de comando em uma variável globalCOMPREPLY
. Se uma solução de pipeline fosse usada para manter novas linhas, o resultado seria perdido. Em sua resposta, temos a impressão de que ambas as soluções são igualmente boas. Além disso, deve-se notar que o comportamento da solução de pipeline depende muito do shell: um usuário pode testarecho foo | { var=$(sed '$s/$/./'); var=${var%.}; } ; echo $var
com ksh93 e zsh e acha que está OK, enquanto esse código está com erros.$var
desaparece" (o que na verdade não é verdade, pois isso depende do shell - o comportamento não é especificado pelo POSIX), que é uma sentença bastante neutra. A segunda solução é melhor porque não sofre desse problema e seu comportamento é consistente em todos os shells POSIX.