Existe uma maneira melhor de desanuviar uma pilha pushd profunda?

1

Às vezes, encontro situações em que algo assim acontece em um script Bash:

pushd /tmp
wget -q http://download.redis.io/redis-stable.tar.gz
tar xzf redis-stable.tar.gz
pushd redis-stable
make
sudo make install
popd; popd

Os bits importantes são os dois pushd s, terminando com dois popd s correspondentes em uma linha. Essa última linha sempre me incomodou; parece que deveria haver uma maneira melhor de expressar a intenção de colocar um número arbitrário de itens fora da pilha e voltar ao diretório original.

Eu pesquisei as páginas man do Bash e tentei variantes dos argumentos popd +n e -n , mas tudo o que eles parecem fazer é remover um item único da pilha a cada vez. Não é isso que eu quero fazer; Eu estou tentando desanuviar a pilha de diretórios, no entanto muitas entradas podem ser profundas, de volta a um estado anterior.

Falta de mudar para um paradigma onde eu salvo OLDPWD=$(pwd) em algum lugar e cd $OLDPWD no final, existe uma maneira melhor de fazer isso?

    
por smitelli 07.08.2016 / 18:01

3 respostas

3

O uso do diretório em scripts é questionável porque é propenso a riscos. popd não retorna ao diretório original, ele retorna ao diretório com o mesmo nome. Se o diretório original foi movido, o seu script irá mudar de localização no meio do caminho. Isso pode parecer um risco remoto, mas mais cedo ou mais tarde isso acontecerá e os usuários do script irão xingar você.

Se você quiser agir temporariamente em um diretório diferente, a melhor maneira é executar essa parte do script em um subshell.

( set -e
  cd "${TMPDIR:-/tmp}"
  wget -q http://download.redis.io/redis-stable.tar.gz
  tar xzf redis-stable.tar.gz
  cd redis-stable
  make
  sudo make install
)

Apenas para o exemplo, mostrarei abordagens alternativas que não são necessárias aqui, mas que podem ser úteis em situações semelhantes em que uma subcamada não é adequada, por ex. porque você deseja definir algumas variáveis e disponibilizá-las depois de retornar ao diretório original.

Você pode não precisar mudar para um diretório diferente ou apenas para a duração de um comando individual. Se você estiver usando ferramentas GNU, tanto tar como make suportam um argumento -C para alternar para um diretório. Como você está criando esse diretório em seu script, ao contrário do diretório original, é razoável supor que o local não será movido.

set -e
: "${TMPDIR:=/tmp}"
wget -q -O "$TMPDIR/redis-stable.tar.gz" http://download.redis.io/redis-stable.tar.gz
tar xzf redis-stable.tar.gz -C "$TMPDIR"
make -C "$TMPDIR/redis-stable"
sudo make -C "$TMPDIR/redis-stable" install

Com comandos que não têm a opção de mudar para um diretório diferente, você pode usar um subshell com um escopo limitado.

set -e
: "${TMPDIR:=/tmp}"
( cd "$TMPDIR"
  wget -q http://download.redis.io/redis-stable.tar.gz
  tar xzf redis-stable.tar.gz -C "$TMPDIR"
)
( cd "$TMPDIR/redis-stable"
  make
  sudo make install
)

Se você realmente precisa alternar, a melhoria óbvia para várias chamadas para popd é chamar cd quando você quer dizer cd , em vez de chamar pushd para um diretório que você não deseja para retornar a.

set -e
pushd "${TMPDIR:-/tmp}"
wget -q http://download.redis.io/redis-stable.tar.gz
tar xzf redis-stable.tar.gz
cd redis-stable
make
sudo make install
popd

Mas eu realmente recomendo não usar pushd e popd . Eles são feitos para uso interativo, e em scripts eles tendem a ser mais confusos do que qualquer coisa. Se você deseja salvar o local do diretório atual, salve-o em uma variável. Isso facilita a leitura do seu script, assim o leitor não precisa descobrir onde a chamada popd retornará.

set -e
original_directory="$PWD"
cd "${TMPDIR:-/tmp}"
wget -q http://download.redis.io/redis-stable.tar.gz
tar xzf redis-stable.tar.gz
cd redis-stable
make
sudo make install
cd "$original_directory"
    
por 08.08.2016 / 00:02
1

Se você tiver uma imagem mental do nível na pilha de diretórios que deseja exibir, use uma função:

function mpopd {
  n=$1
  while [[ $n > 0 ]]
  do
    popd
    n=$((n-1))
  done
}

Execução da amostra:

[schaller@host smitelli] dirs
~/tmp/smitelli
[schaller@host smitelli] pushd tmp/
~/tmp/smitelli/tmp ~/tmp/smitelli
[schaller@host tmp] pushd redis-stable/
~/tmp/smitelli/tmp/redis-stable ~/tmp/smitelli/tmp ~/tmp/smitelli
[schaller@host redis-stable] mpopd 2
~/tmp/smitelli/tmp ~/tmp/smitelli
~/tmp/smitelli
    
por 07.08.2016 / 20:11
0

Você pode simplesmente iniciar um novo shell filho antes de iniciar seu trabalho e, em seguida, sair dele no final, retornando ao shell e ao diretório original.

Mas talvez seja melhor manter o histórico do diretório, já que muitas vezes você precisa repetir o mesmo pushd depois. Na verdade, eu alias cd ao equivalente a pushd , depois uso outras funções para navegar facilmente pelo dirstack sem perder nada disso. Ao invés disso, você pode fazer algo simples, como uma função para listar sua pasta e perguntar para onde ir:

cdp(){
  select v in $(dirs -l -p)
  do pushd "$v"
     break
  done
}
    
por 07.08.2016 / 21:21