Entenda a substituição complexa de comandos com {} e vários `\ls`

Entenda a substituição complexa de comandos com {} e vários `\ls`

Estou tentando entender esta linha a partir de um script de shell. Eu sei que isso $(..)significa executar ..e inserir sua saída onde você encontrar $()em uma instrução. Mas o que está acontecendo entre esses parênteses? O que é o \lsfazer e como isso se relaciona com o que está acontecendo \nas linhas anteriores? Isso é uma \\divisão em duas linhas? É \lso mesmo que normal ls?

APPCLASSPATH=$CLASSPATH:$({ \
    \ls -1 "$VOLTDB_VOLTDB"/voltdb-*.jar; \
    \ls -1 "$VOLTDB_LIB"/*.jar; \
    \ls -1 "$VOLTDB_LIB"/extension/*.jar; \
} 2> /dev/null | paste -sd ':' - )

Responder1

A saída dos 3 lscomandos é passada para o pastecomando que os mescla no valor:

$VOLTDB_VOLTDB"/voltdb-*.jar:$VOLTDB_LIB"/*.jar:$VOLTDB_LIB"/extension/*.jar

OBSERVAÇÃO:As variáveis $VOLTDB_VOLTDB​​e $VOLTDB_LIBserão expandidas e poderá haver mais valores do que apenas um arquivo para cada um desses lscomandos. Veja *lá? Esse é um caractere glob que atua como curinga e se expande para qualquer coisa entre o lado esquerdo (voltdb-) e o lado direito (.jar), por exemplo.

Eles corresponderiam a:

voltdb-1.jar
voltdb-blah.jar
voltdb-12345.jar

Tudo é então incluído na variável APPCLASSPATH:

APPCLASSPATH=$CLASSPATH:$VOLTDB_VOLTDB"/voltdb....etc.

O comando colar

Aqui está um exemplo em que estou usando o seqcomando para gerar uma sequência de números de 1 a 10.

$ seq 10 | paste -sd ':' -
1:2:3:4:5:6:7:8:9:10

Você pode ver que o pastecomando está mesclando a saída e separando-a com dois pontos ( :).

Você também pode imitar o comando de exemplo assim:

$ { echo "hi1"; echo "hi2"; echo "hi3"; } | paste -sd ':' -
hi1:hi2:hi3

OBSERVAÇÃO:O -comando to the paste diz para ele pegar a entrada do STDIN e imprimir cada argumento conforme ele chega, separado por a :.

Com diferentes opções, pastetambém é possível dividir os dados em grupos, com base no número de -'s depois deles.

Colar exemplos

Aqui está um exemplo com 2 -'s.

$ seq 10 | paste - -
1       2
3       4
5       6
7       8
9       10

Aqui estão 3 -.

$ seq 10 | paste - - -
1       2       3
4       5       6
7       8       9
10

Portanto, está dizendo pastequantos argumentos pastedevem ser impressos em cada linha. Mas não se confunda, o exemplo com o qual você está lidando é simplesmente pegar a entrada do STDIN, separar cada argumento em espaços e imprimi-lo seguido por a :. Ao fornecer vários -, você está dizendo pastepara receber argumentos, 2 de cada vez, 3 de cada vez, etc.

Argumentos 2 de cada vez, separados por :'s:

$ seq 10 | paste -d ':' - -
1:2
3:4
5:6
7:8
9:10

$ seq 10 | paste -d ':' - - -
1:2:3
4:5:6
7:8:9
10::

Aliás, se você incluir a -sopção, estará dizendo pastepara levar os argumentos em série (em série). Veja o que acontece quando você o usa em um dos exemplos acima.

2 de cada vez:

$ seq 10 | paste -sd ':' - -
1:2:3:4:5:6:7:8:9:10

3 de cada vez:

$ seq 10 | paste -sd ':' - - -
1:2:3:4:5:6:7:8:9:10

Responder2

$(command)executa um comando e substitui sua saída.

{ list; }é um comando de grupo que executa vários comandos no ambiente shell atual. É semelhante a (list), mas não forma um subshell.

\commandé usado para ignorar aliases para comandos, o que pode alterar consideravelmente o comportamento esperado de um comando.

O \no final da linha significa simplesmente que esta linha continua, então o shell verá a próxima linha como parte da atual. Geralmente não é necessário quando isso é óbvio no contexto (parênteses abertos ou citação).

Responder3

APPCLASSPATH=$CLASSPATH:$({ \
    \ls -1 "$VOLTDB_VOLTDB"/voltdb-*.jar; \
    \ls -1 "$VOLTDB_LIB"/*.jar; \
    \ls -1 "$VOLTDB_LIB"/extension/*.jar; \
} 2> /dev/null | paste -sd ':' - )

\lsé como ls, exceto que se lsfor um alias, a barra invertida impede a expansão do alias. Isso garante que o lscomando seja usado e não algum alias que possa adicionar saída indesejada, como um sufixo classificador ( -F).

Os lscomandos, chamados com nomes de arquivos existentes como argumentos, listam seus argumentos, um por linha. A opção -1não tem efeito pois a saída de lsvai para um tubo e não para um terminal. Se lsreceber um argumento que não seja o nome de um arquivo existente, ele não exibirá nada em sua saída padrão e, em vez disso, exibirá um erro. Erros dos lscomandos são redirecionados para lugar nenhum pelo 2> /dev/null. Há dois motivos pelos quais lspode receber um argumento que não é um arquivo: se uma das variáveis ​​não se referir a um diretório legível existente ou se não houver nenhum arquivo que corresponda ao padrão curinga. Em ambos os casos, o padrão é passado sem expansão para ls.

As barras invertidas no final das linhas fazem com que o shell ignore a nova linha a seguir. Nenhum deles é útil aqui, pois em cada ponto onde são usados, o shell espera uma nova linha opcional.

As chaves {…} agrupam os comandos. O comando composto { \ls …; \ls …; \ls … ; }é canalizado pastee tem seus erros redirecionados para /dev/null.

O pastecomando une todas as linhas de entrada com um :ponto intermediário. É equivalente a, tr '\n' :exceto que não coloca um :no final.

A substituição do comando $(…)faz com que a saída de pasteseja interpolada em APPCLASSPATH, após o valor da CLASSPATHvariável com dois pontos para separar as duas partes.

Aqui está uma versão simplificada. Isso é um pouco diferente do original porque, se nenhum dos padrões curinga corresponder a nada, APPCLASSPATHserá igual a CLASSPATHsem dois pontos extras no final (o que provavelmente é desejável).

APPCLASSPATH=$CLASSPATH:$(
  \ls "$VOLTDB_VOLTDB"/voltdb-*.jar "$VOLTDB_LIB"/*.jar "$VOLTDB_LIB"/extension/*.jar |
  tr '\n' :) 2>/dev/null
APPCLASSPATH=${APPCLASSPATH%:}

informação relacionada