Por que não consigo redirecionar uma saída de nome de caminho de um comando para “cd”?

27

Estou tentando cdaceitar um nome de diretório redirecionado para ele a partir de outro comando. Nenhum desses métodos funciona:

$ echo $HOME | cd
$ echo $HOME | xargs cd

Isso funciona:

$ cd $(echo $HOME)

Por que o primeiro conjunto de comandos não funciona e existem outros que também falham dessa maneira?

Jhonathan
fonte
Por outras pessoas, você está se referindo a outros comandos ou outros métodos para usar cd que falham dessa maneira?
Didi Kohen
@DavidKohen Refiro-me a outros comandos
Jhonathan
Alguns exemplos notáveis ​​são ulimit, umask, popd, pushd, set, export e read.
Didi Kohen

Respostas:

32

cdnão é um comando externo - é uma função interna do shell. Ele é executado no contexto do shell atual e não, como comandos externos, em um contexto fork / exec'd como um processo separado.

Seu terceiro exemplo funciona, porque o shell expande a variável e a substituição do comando antes de chamar o cdbuiltin, para que cdreceba o valor ${HOME}como argumento.

Sistemas POSIX fazer ter um binário cd- na minha máquina FreeBSD, é a /usr/bin/cd, mas não faz o que você pensa. Chamar o binário cdfaz com que o shell bifurque / execute o binário, o que de fato altera seu diretório de trabalho para o nome que você passa. No entanto, assim que o faz, o binário sai e o processo bifurcado / exec desaparece, retornando você ao seu shell, que ainda está no diretório em que estava antes de iniciar.

D_Bye
fonte
10
O que nos faz pensar Qual é o sentido do cdcomando externo ?
Kojiro # 0512
23

cdnão lê a entrada padrão. É por isso que seu primeiro exemplo não funciona.

xargsprecisa de um nome de comando, ou seja, um nome de um executável independente. cdprecisa ser um comando interno do shell e não teria efeito (além de verificar se você pode mudar para esse diretório e os possíveis efeitos colaterais que ele pode ter para diretórios de montagem automática) se fosse um executável. É por isso que o seu segundo exemplo não funciona.

mouviciel
fonte
4

Além da boa resposta existente, também vale a pena mencionar que um pipe bifurca um novo processo, que possui seu próprio diretório de trabalho separado. Portanto, tentar fazer isso não funcionará:

echo test | cd /

Portanto, você não estará na pasta / depois que o shell retornar deste comando.

Mark Rejhon
fonte
Todos os comandos em uma corrida gasoduto em diferentes processos, de modo em a | b, mesmo se ae bestá embutido, pelo menos um não, então não executar no processo shell, mas não há garantia de que qual deles é. Por exemplo, na AT&T ksh, zshou bash -O lastpipe, bé executado no processo atual do shell, portanto, seu código o levaria para / lá.
Stéphane Chazelas
4

Além das respostas corretas já fornecidas: Se você executa o bash e deseja descobrir o que é um "comando" como o cd, pode usar o tipo

$ type cd
cd is a shell builtin

ou porque não:

$ type time
time is a shell keyword

enquanto, por exemplo, o tempo gnu normalmente já está incluído na sua distribuição favorita:

$ which time
/usr/bin/time

Okey okey, você entendeu a ideia, então que diabos é esse tipo?

$ type type
type is a shell builtin

Aqui está um snippet manual do bash:

       type [-aftpP] name [name ...]
          With no options, indicate how each name would be interpreted  if  used  as  a
          command name.  If the -t option is used, type prints a string which is one of
          alias, keyword, function, builtin,  or  file  if  name  is  an  alias,  shell
          reserved word, function, builtin, or disk file, respectively.  If the name is
          not found, then nothing is printed, and an exit status of false is  returned.
          If  the -p option is used, type either returns the name of the disk file that
          would be executed if name were specified as a command  name,  or  nothing  if
          ‘‘type  -t  name’’ would not return file.  The -P option forces a PATH search
          for each name, even if ‘‘type -t name’’ would not return file.  If a  command
          is  hashed,  -p  and -P print the hashed value, not necessarily the file that
          appears first in PATH.  If the -a option is used,  type  prints  all  of  the
          places  that  contain  an  executable  named name.  This includes aliases and
          functions, if and only if the -p option is  not  also  used.   The  table  of
          hashed  commands  is  not  consulted when using -a.  The -f option suppresses
          shell function lookup, as with the command builtin.  type returns true if any
          of the arguments are found, false if none are found.
3molo
fonte
0

Como já foi dito, ele não funcionará porque cdé um comando interno do shell, não um programa externo; portanto, não possui nenhuma entrada padrão na qual você possa inserir algo.

Mas, mesmo que funcionasse, não faria o que você deseja: um canal gera um novo processo e redireciona a saída padrão do primeiro comando para a entrada padrão do segundo, portanto, apenas o novo processo alterará seu funcionamento atual diretório; isso não poderia afetar o primeiro processo de forma alguma.

Massimo
fonte
2
Seu segundo parágrafo está certo. Mas com relação ao primeiro parágrafo: por que você supõe que os shellins não têm stdin? readgeralmente é (sempre?) um shell embutido. É verdade que cdignora stdin, mas isso não se deve ao fato de ser um builtin.
dubiousjim
-2

Outra opção são backticks, que colocam o stdout de um comando como argumento da linha de comando de um segundo comando e são mais portáteis que $(...). Por exemplo:

cd `echo $HOME`

ou mais geralmente;

cd `anycommand -and whatever args`

Observe que o uso de backticks depende do shell para executar o comando e substituir a saída na linha de comando. A maioria das conchas suporta isso.

Seth Noble
fonte
3
O OP já declarou, em sua pergunta, que $(...)funciona. Eu não acho que seja um bom conselho recomendar backticks, pois eles têm regras de cotação muito mais complicadas e geralmente são mais propensas a erros. (Veja §3.5.4 "Substituição de comando" no Manual de referência do Bash .)
ruakh
$ () é bom quando suportado, mas os backticks são mais amplamente suportados em diferentes shells e sistemas. Mas devo reformular isso como "outra opção".
Seth Noble
Conchas diferentes, sim; mas diferentes "sistemas"? Existe realmente alguma concha que suporte $(...)em um sistema, mas não em outro?
ruach
4
-1, ele não responde à pergunta.
Bernhard
3
@ruakh & Seth: Todos os shells POSIX suportam $(…). Os sistemas sem um shell POSIX (ou seja, com um shell Bourne genuíno) seriam extremamente antigos. Mesmo sistemas onde /bin/shexiste um shell Bourne e você precisa de outro caminho, como /usr/xpg4/bin/shum shell POSIX, são raros hoje em dia. A recomendação de backticks para quem não administra profissionalmente o antigo unix boxen está fazendo um desserviço.
Gilles 'SO- stop be evil'