8

When I run this command it works:

$ find . -inum 888696 -exec ls '{}' \;
Conversation.pst  Outlook Data File  Outlook Data File.sbd  Trash      Unsent Messages
Inbox.pst     Outlook Data File.msf  Sent.pst       Trash.msf  Unsent Messages.msf

However, When replacing ls with cd it does not work:

$ find . -inum 888696 -exec cd '{}' \;
find: ‘cd’: No such file or directory

I know cd is a bash built-in, so I tried this which does not work either:

$ find . -inum 888696 -exec builtin cd '{}' \;
find: ‘builtin’: No such file or directory

How can I use cd along with find -exec command?


UPDATE

The reason I'm trying to use cd with find -exec is that the directory name is a strange one which shows up on my terminal as something like ????.

Megidd
  • 1,549
  • 1
    BTW, you can LC_ALL=C printf '%q\n' * to print ASCII names for all files in your current directory, one to a line (changing newlines to $'\n' or similar). – Charles Duffy Jun 05 '18 at 19:53

5 Answers5

15

The -exec option to find executes an external utility, possibly with some command line option and other arguments.

Your Unix does not provide cd as an external utility, only as a shell built-in, so find fails to execute it. At least macOS and Solaris do provide cd as an external utility.

There would be little or no use for executing cd in this way, except as a way of testing whether the pathname found by find is a directory into which you would be able to cd. The working directory in your interactive shell (or whatever is calling find) would not change anyway.

Related:


If you're having issues with a directory's name being strange or extremely difficult to type, and you want to change into that directory, then consider creating a symbolic link to the directory and then cd into it using that link instead:

find . -inum 888696 -exec ln -s {} thedir ';'

This would create a symbolic link named thedir that would point to the problematic directory. You may then change working directory with

cd thedir

(if the link exists in the current directory). This avoids modifying the directory in any way. Another idea would be to rename the directory in a similar way with find, but that would not be advisable if another program expects the directory to have that particular name.

Kusalananda
  • 333,661
  • The reason I intent to use cd with find -exec is that the directory names are in some strange characters which don't show up correctly on my terminal. – Megidd Jun 05 '18 at 11:29
  • @user3405291 It is not clear from the question what you expect to happen when you run the command. Do you expect to change directory in the interactive shell? – Kusalananda Jun 05 '18 at 11:30
  • Yes, I just want to cd into a directory which has a bad name, and I cannot cd into it in a normal way. – Megidd Jun 05 '18 at 11:32
  • @user3405291 See update. – Kusalananda Jun 05 '18 at 11:36
  • What's funny is that /bin/cd is a result of POSIX (http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap01.html#tag_17_06) where the normal builtins have to be accessible to exec(). Of course, /bin/cd likely doesn't do what people want :-) – Stephen Harris Jun 05 '18 at 11:37
  • @StephenHarris The Solaris cd manual mentions that about the only use for the external cd utility is to test whether changing working directory would be allowed or not. – Kusalananda Jun 05 '18 at 11:38
  • Regarding the bad characters, you can always use wildchards. You can use "ls -b" to find out the "bad" characters, and replace them by dots for your cd command, eg. "cd bd.....dir". – Gerard H. Pille Jun 05 '18 at 11:41
  • @GerardH.Pille Well, that works if you can type any of the name's characters. I'm imagining the situation where you actually can't. My solution also results in a handy symbolic link that would be easy to use many times around, and it leaves the original directory unmodified (in case something depend upon its name). – Kusalananda Jun 05 '18 at 11:42
  • The symbolic link solution is simple and easy to remember. Thanks. – Megidd Jun 05 '18 at 11:45
  • @user3405291 can you give us an example of such a name? The "ls -b" should allow you to cut and paste it in your question. I'm just asking to see if the problem is not with your terminal settings iso with the directory. – Gerard H. Pille Jun 05 '18 at 11:49
  • @GerardH.Pille, just mind that ls -b escapes "nongraphic" characters, and that concept might still include some pretty weird things in these times and ages... e.g. Hebrew or emojis may be somewhat difficult to enter on an English keyboard, even though they're still quite much "graphical" – ilkkachu Jun 05 '18 at 11:56
  • @GerardH.Pille Thanks. I was not aware of ls -b, however looks like the directory name is more weird in my case. – Megidd Jun 05 '18 at 11:57
  • @user3405291 if it's that weird, you'd better not show it to us. You can always "srm" it. – Gerard H. Pille Jun 05 '18 at 12:54
  • @GerardH.Pille What is srm? Well, I know I can google it ... :) – Megidd Jun 05 '18 at 13:04
7

find runs the -exec command itself, it doesn't involve a shell. Even if it did, the change of directory would only persist until that shell exited, immediately after the cd.

You'll need to get the filename out to the current shell to cd into it. Depending on how bad your filenames are, you could use command substitution:

cd "$(find . -inum 888696)"

That won't work if the filename ends in a newline, as command substitution eats trailing newlines. In that case you'd need to protect the newline and get rid of the one find adds when printing:

dir=$(find . -inum 888696; echo x)
cd "${dir%?x}"

Or, with GNU find, have it not print the trailing newline (but still protect any in the filename):

dir=$(find . -inum 888696 -printf "%px" -quit)
cd "${dir%x}"

Also using the -quit predicate (also a GNU extension), to stop looking after the first match as an optimisation.

Alternatively, you could start a new shell from within find, but it's a bit ugly:

find . -inum 888696 -exec bash -c 'cd "$1" && exec bash' sh {} \;
ilkkachu
  • 138,973
  • Tried your trick with the "echo x", on directories ending with newline, carriage return and both, without success. – Gerard H. Pille Jun 05 '18 at 12:13
  • @GerardH.Pille, oh, sorry, I forgot the newline find itself adds when printing. Edited. – ilkkachu Jun 05 '18 at 12:22
  • If you replace the printf by a print0, you can do ' cd "$dir" '. – Gerard H. Pille Jun 05 '18 at 12:51
  • @GerardH.Pille, perhaps. But Bash ignores any nul bytes in input from a command substitution, and removes the trailing newline only after that. So dir=$(find -print0) will still trash the trailing newline of the filename... – ilkkachu Jun 05 '18 at 13:00
5

Not with exec, but this may be good enough for you:

cd "$(find . -inum 888696 -type d)"

The "-type d", just to be sure. Of what, I don't really know.

5

Use a NUL-delimited stream to read output from find that works in all cases -- including names that end in newlines. Also, you can use printf '%q' to generate a readable representation of a filename.

inum=888696
if IFS= read -r -d '' filename < <(find . -inum "$inum" -print0); then
  LC_ALL=C printf 'Located filename: %q\n' "$filename" >&2
  cd -- "$filename"
else
  echo "No file located for inode $inum" >&2
fi
Charles Duffy
  • 1,732
  • 15
  • 22
3

If you get this message, then your OS platform is buggy. The POSIX standard requires that a command named cd must be available in the file system so it may be called via exec().

Now the bad news for you:

Even if your OS platform was not buggy, you just did not see a warning, but you did not get your expected results, since it does not help you if a separate program changes its current working directory and immediately dies after that.

If you like to have an effective cd in a command executed by find, you may do something like:

find . -type d -exec sh -c 'cd "$1"; some other command' dummy {} \;
schily
  • 19,173