0

I am seeing "xargs" behaviour that seems to be quite unexpected, in my experience. I use "ls" to find a matching folder name and sending it to "cd" using "xargs". I get the message that the folder doesn't exist. If I don't use "xargs", however, the "cd" works. Also, if I do use "xargs" but send it to "ls" instead of "cd", it also works.

# Identify desired directory
ls -d *[Aa]nt*

201909+ants/

Print the desired command with "echo"

ls -d [Aa]nt | xargs echo cd

cd 201909+ants/

Execute the desired command and get error

ls -d [Aa]nt | xargs cd

xargs: cd: No such file or directory

Confirm that the desired command without "xargs"

cd 201909+ants # This works no problem cd .. # Go back up cd 201909+ants/ # This works no problem cd .. # Go back up

Confirm that "xargs" with "ls"

ls -d [Aa]nt | xargs ls -d

201909+ants/

I'm convinced that this is an ID1OT problem between the keyboard and chair. Can anyone point out where my error lies?

I am using Cygwin's Bash, but rarely do I see a disparity with Linux. I can't remember the last time I saw such a disparity.

  • 4
    cd is necessarily a shell built-in (it changes the directory used by following commands in the same shell process). xargs will only run external commands (and there is no /bin/cd). There is also no purpose in trying to run -exec cd, because even if it worked, there is no other command to run in the new directory. – Paul_Pedant Jul 01 '22 at 22:08
  • Thanks, Paul_Pedant. This makes perfect sense. When I issue "which cd", it is not found. And of course, "xargs" can't emulate a command that is built-in to Bash. Would you please post this as the answer? Thanks! P.S. My solution is cd $(ls -d *[Aa]nt*), which I find more cognitively burdensome than "xargs". But for this simple usage, not so much. – user2153235 Jul 01 '22 at 22:12
  • 1
    With bash, type cd will tell you more specifically it is a builtin and adding -a will implicitly confirm it is not also external (which it is, mostly uselessly, on systems that unlike cygwin and many Linux are fully POSIX conformant; there are several long-existing Qs about this). However, simply cd *[Aa]nt* should be easy to type; or cd *[Aa]nt*/ to match only a directory especially if you have failglob set, although if the former matches a file cd will just say so. PS: stdio is not involved in xargs running things at all. – dave_thompson_085 Jul 02 '22 at 03:07
  • 1
    See Why not parse ls (and what to do instead)?. BTW, even if there was a /bin/cd external command, xargs is a child process of your shell (and /bin/cd would, in turn, be a child of xargs) and a child process can not affect the environment (including current working dir) of its parent. a hypothetical /bin/cd couldn't affect the env of xargs, and xargs couldn't affect your shell's env. (that's actually why there's no point in anyone writing a /bin/cd program - it's inherently useless). – cas Jul 02 '22 at 03:33
  • 1
    Read the error message again: it is not cd saying that the directory dose not exist. It is xargs saying that cd does not exist. – ctrl-alt-delor Jul 02 '22 at 11:49

1 Answers1

1

The working directory is a property of the running process, and changes to it have to be made within the process itself. Hence, cd is a builtin command in the shell. But xargs isn't (well, usually), and it can't run shell builtins. What it does do, is to call an external program.

Because cd doesn't really work as an external program, it's not generally available as such on Linux systems. That's why you get the error.

On other systems it might be, because some specifications say It Has To Be. See Why is cd not a program?. It's not very useful though, it'll just change the working directory of the cd process, which then immediately exits.

But, you don't need ls here, or xargs. If you do ls *.txt or such, it's the shell that creates a list of the filenames matching the pattern, that list gets passed to ls, which... just prints them out. Pretty much the same as if you did echo *.txt. (ls -l *.txt would make more sense, since there, ls does some actual work in finding all the other data about the files.) So, instead of ls *[Aa]nt* |xargs, you could (roughly) use echo *[Aa]nt* | xargs and that's also redundant, since echo prints it's command line, and xargs turns it input into a command line for another program.

In the simple cases, you should be able to just go with

cd *[Aa]nt*

But if the pattern matches more than one filename, you'll get an error (or strange behaviour in some shells, e.g. in ksh cd foo bar does a string replacement on the current working directory path.)

(of course ls *.txt prints the filenames one per line when redirected to a pipe, but echo *.txt prints them all on one line so they're not exactly the same.)

ilkkachu
  • 138,973