views:

73

answers:

4

In a shell script how would I find a file by a particular name and then navigate to that directory to do further operations on it?

From here I am going to copy the file across to another directory (but I can do that already just adding it in for context.)

Thanks in advance :)

+3  A: 

You can use something like:

pax[/home/pax]> cd "$(dirname "$(find / -type f -name ls | head -1)")"
pax[/usr/bin]> _

This will locate the first ls regular file then change to that directory.

In terms of what each bit does:

  • The find will start at / and search down, listing out all regular files (-type f) called ls (-name ls). There are other things you can add to find to further restrict the files you get.
  • The piping through head -1 will filter out all but the first.
  • $() is a way to take the output of a command and put it on the command line for another command.
  • dirname can take a full file specification and give you the path bit.
  • cd just changes to that directory.

If you execute each bit in sequence, you can see what happens:

pax[/home/pax]> find / -type f -name ls
/usr/bin/ls

pax[/home/pax]> find / -type f -name ls | head -1
/usr/bin/ls

pax[/home/pax]> dirname "$(find / -type f -name ls | head -1)"
/usr/bin

pax[/home/pax]> cd "$(dirname "$(find / -type f -name ls | head -1)")"

pax[/usr/bin]> _
paxdiablo
could you explain what is happening please?
Ross Alexander
@Ross: sure thing, there you go.
paxdiablo
I think you need a space between `-type f` and `-name ls`.
Dennis Williamson
@Ross: note that there should be double quotes around each `$()` (otherwise your script will fail with paths containing whitespace and other special characters): `cd "$(dirname "$(find / -type f-name ls | head -1)")"`
Gilles
That's a good point, Gilles, updated to fix. I rarely put spaces in my file specifications since I think they're abominable, but I realise some people like them and you're right: code should handle them.
paxdiablo
It would still break on directory names containing newlines.
Philipp
Yes, it would, and people using newlines and backspaces and tabs and other funny characters in their file names should be beaten to death, separated into small bits and have those bits launched to the furthest reaches of the universe to burn in the hearts of various stars :-)
paxdiablo
@paxdiablo: You can't always decide what characters are going to end up in a file name. Sometimes your scripts encounter other people's files. Sometimes you're on Windows and need to treat `c:\Program Files` correctly.
Gilles
Any program that pretends to work with file names but doesn't accept all valid file names is flawed.
Philipp
A: 

if you are just finding the file and then moving it elsewhere, just use find and -exec

find /path -type f -iname "mytext.txt" -exec mv "{}" /destination +;
ghostdog74
A: 

If it's a program in your PATH, you can do:

cd "$(dirname "$(which ls)")"

or in Bash:

cd "$(dirname "$(type -P ls)")"

which uses one less external executable.

This uses no externals:

dest=$(type -P ls); cd "${dest%/*}"
Dennis Williamson
@Dennis: in which shell does `cd` not require quotes? After `export d="foo bar"; mkdir "$d"`: `ksh -c 'cd $d'` complains `cd: bad substitution`; `ash -c 'cd $d'` complains: `can't cd to foo`; `bash -c 'cd $d'` complains: `cd: foo: Not a directory`.
Gilles
@Gilles: I could have sworn that worked in Bash. I'll edit my answer.
Dennis Williamson
@Dennis: it can't work in a POSIX-conforming shell, `cd` has no special dispensation from the usual expansion rules. And you're still missing the quotes around the argument to `dirname`.
Gilles
+1  A: 

The following should be more safe:

cd -- "$(find / -name ls -type f -printf '%h' -quit)"

Advantages:

  • The double dash prevents the interpretation of a directory name starting with a hyphen as an option (find doesn't produce such file names, but it's not harmful and might be required for similar constructs)
  • -name check before -type check because the latter sometimes requires a stat
  • No dirname required because the %h specifier already prints the directory name
  • -quit to stop the search after the first file found, thus no head required which would cause the script to fail on directory names containing newlines
Philipp