As other answers have already identified, ${f%.*}
is expanded by the shell before it runs the xargs
command. You need this expansion to happen once for each file name, with the shell variable f
set to the file name (passing -I f
doesn't do that: xargs
has no notion of shell variable, it looks for the string f
in the command, so if you'd used e.g. xargs -I e echo …
it would have executed commands like ./somedir/somefile.wmacho .mp3
).
Keeping on this approach, tell xargs
to invoke a shell that can perform the expansion. Better, tell find
— xargs
is a largely obsolete tool and is hard to use correctly, as modern versions of find have a construct that does the same job (and more) with fewer plumbing difficulties. Instead of find … -print0 | xargs -0 command …
, run find … -exec command … {} +
.
find . -name '*.wma' -type f -exec sh -c 'for f; do echo "${f%.*}.mp3"; done' _ {} +
The argument _
is $0
in the shell; the file names are passed as the positional arguments which for f; do …
loops over. A simpler version of this command executes a separate shell for each file, which is equivalent but slightly slower:
find . -name '*.wma' -type f -exec sh -c 'echo "${0%.*}.mp3"' {} \;
You don't actually need to use find
here, assuming you're running a reasonably recent shell (ksh93, bash ≥4.0, or zsh). In bash, put shopt -s globstar
in your .bashrc
to activate the **/
glob pattern to recurse in subdirectories (in ksh, that's set -o globstar
). Then you can run
for f in **/*.wma; do
echo "${f%.*}.mp3"
done
(If you have directories called *.wma
, add [ -f "$f" ] || continue
at the beginning of the loop.)
If the author was trying to catch you out by talking about that literal string (without shell expansion) as a path, then it's a relative path (mkdir -p './~/Documents'
). Otherwise:
It's an absolute path, because resolving it doesn't depend on the process's current working directory. Relative path always means relative to the process's working directory. Or in the case of symlink targets, relative to the location of the symlink. (gcc -> gcc-5.2
vs. gcc -> /usr/bin/gcc-5.2
). This matters for NFS-mounts and other cases where you can get to the same symlink via different absolute paths. e.g.
/net/tesla/home/peter/foo -> bar # always works from other machines
/net/tesla/home/peter/foo -> /home/peter/bar # references my home dir on the local machine, not tesla.
Debian will sometimes install symlinks to ../../doc/whatever/whatever
, instead of an absolute symlink target, so it works when NFS mounted somewhere else, or when looking at a chroot without chroot(8)
ing into it.
Every Unix process has its own cwd. The pwd
command exists just to print it.
see: http://pubs.opengroup.org/onlinepubs/9699919799/functions/getcwd.html
for more about changing directories with POSIX system calls.
As everyone else has said, ~
is expanded by the shell before the path is used for anything. Using ~/bin/myprog
in a shell script will make it work differently for different users. The difference between ~/bin/foo
and /home/peter/bin/foo
is that one of them has hard-coded the location, while the other has parameterized it. It's an error (IMO) to call the ~
version a relative path.
Talking about things being "relative to an environment variable" is just confusing. It's bad practice to use different English-language meanings of terms that have specific technical meanings in the context you're using them in.
On a broken system, with HOME=a/relative/path
, ~/foo
would expand to a relative path. This would not be a usable setup at all.
Best Answer
Use find with an absolute path.
It will print the whole path.
If you do not know the working directory then use command substitution for
pwd
like this:To get your working directory
Anyway, it seems that Bash man now advise to use
$()
over``
so you should use the second form. You can also probably refer directly to the$PWD
variable that contains the working directory of your script and it would be probably faster if you have to use in a loop.