7

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 ????.

  • 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 5 '18 at 19:53
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.

  • 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. – user3405291 Jun 5 '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 5 '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. – user3405291 Jun 5 '18 at 11:32
  • @user3405291 See update. – Kusalananda Jun 5 '18 at 11:36
  • What's funny is that /bin/cd is a result of POSIX (pubs.opengroup.org/onlinepubs/9699919799/utilities/…) where the normal builtins have to be accessible to exec(). Of course, /bin/cd likely doesn't do what people want :-) – Stephen Harris Jun 5 '18 at 11:37
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.

  • This fails if the directory's name ends with a newline. – Kusalananda Jun 5 '18 at 11:39
  • Sure, but directories rarely do. If I try to create one on a linux ext4, I get "protocol error". Don't you think this is a waste of time? – Gerard H. Pille Jun 5 '18 at 11:43
  • 2
    Well, names rarely have unprintable characters, but this one obviously do. – Kusalananda Jun 5 '18 at 11:48
  • What filesystems allow newlines in directory names? – Gerard H. Pille Jun 5 '18 at 11:52
  • 1
    @GerardH.Pille, how did you test that? mkdir $'foo\n' works perfectly here; I've yet to see a native UNIX filesystem where it wasn't supported. – Charles Duffy Jun 5 '18 at 17:25
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 {} \;
  • Tried your trick with the "echo x", on directories ending with newline, carriage return and both, without success. – Gerard H. Pille Jun 5 '18 at 12:13
  • @GerardH.Pille, oh, sorry, I forgot the newline find itself adds when printing. Edited. – ilkkachu Jun 5 '18 at 12:22
  • If you replace the printf by a print0, you can do ' cd "$dir" '. – Gerard H. Pille Jun 5 '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 5 '18 at 13:00
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 {} \;
  • 2
    That is only a bug if that platform claims POSIX conformance. Having a standalone cd utility is not very useful, so it makes sense to ignore that requirement otherwise without it affecting the usability of the platform. Note that leaving a parameter expansion unquoted has a very special meaning in sh, not something you'd want to do. It's better to avoid dummy values for that inline script's $0 as that's used in error messages for instance (like when that cd would fail). – Stéphane Chazelas Jun 5 '18 at 12:34
  • Did you mean cd "$1"? If the name is hard to type, it may well also contain shell metacharacters... – Toby Speight Jun 6 '18 at 11:21
  • correct this was a typo – schily Jun 6 '18 at 11:57
4

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.