With zsh
:
dirs=(*(/))
mkdir -- $^dirs/doc
touch -- $^dirs/doc/doc1.txt
(/)
is a globbing qualifier, /
means to select only directories.
$^array
(reminiscent of rc
's ^
operator) is to turn on a brace-like type of expansion on the array, so $^array/doc
is like {elt1,elt2,elt3}/doc
(where elt1
, elt2
, elt3
are the elements of the array).
One could also do:
mkdir -- *(/e:REPLY+=/doc:)
touch -- */doc(/e:REPLY+=/doc1.txt:)
Where e
is another globbing qualifier that executes some given code on the file to select.
With rc
/es
/akanga
:
dirs = */
mkdir -- $dirs^doc
touch -- $dirs^doc/doc1.txt
That's using the ^
operator which is like an enhanced concatenation operator.
rc
doesn't support globbing qualifiers (which is a zsh-only feature). */
expands to all the directories and symlinks to directories, with /
appended.
With tcsh
:
set dirs = */
mkdir -- $dirs:gs:/:/doc::q
touch -- $dirs:gs:/:/doc/doc1.txt::q
The :x
are history modifiers that can also be applied to variable expansions. :gs
is for global substitute. :q
quotes the words to avoid problems with some characters.
With zsh
or bash
:
dirs=(*/)
mkdir -- "${dirs[@]/%/doc}"
touch -- "${dirs[@]/%/doc/doc1.txt}"
${var/pattern/replace}
is the substitute operator in Korn-like shells. With ${array[@]/pattern/replace}
, it's applied to each element of the array. %
there means at the end.
Various considerations:
dirs=(*/)
includes directories and symlinks to directories (and there's no way to exclude symlinks other than using [ -L "$file" ]
in a loop), while dir=(*(/))
(zsh extension) only includes directories (dir=(*(-/))
to include symlinks to directories without adding the trailing slash).
They exclude hidden dirs. Each shell has specific option to include hidden files).
If the current directory is writable by others, you potentially have security problems. As one could create a symlink there to cause you to create dirs or files where you would not want to. Even with solutions that don't consider symlinks, there's still a race condition as one may be able to replace a directory with a symlink in between the dirs=(*/)
and the mkdir...
.
These examples work in any POSIX shell and require no external programs.
This stores the Part*.mp3 files at the same level as the Project directory:
(cd Project && for i in Part*/audio.mp3; do echo mv "$i" ../"${i%/*}".mp3; done)
This keeps the Part*.mp3 files in the Project directory:
for i in Project/Part*/audio.mp3; do echo mv "$i" ./"${i%/*}".mp3; done
These solutions use the shell's pattern matching
parameter expansion
to produce the new filename.
${parameter%word} Remove Smallest Suffix Pattern. The word is expanded
to produce a pattern. The parameter expansion then
results in parameter, with the smallest portion of
the suffix matched by the pattern deleted.
Best Answer
Try the
execdir
option forfind
: it executes the command you specify in the directory of the file, using only its basename as the argumentFrom what I gather, you want to create "a" and "b" in the "main" directory. We can do that by combining
$PWD
and the-execdir
option. Have a look at the solution below. (The&& find … ls
parts are for output only, so you can see the effects. You'll want to use the command before the&&
.)First, I set up the testing environment:
This is what happens when you use a simple
-exec
— the original files are touched:However, if we combine
$PWD
with the argument placeholder{}
and use-execdir
, we achieve what (I think) you want: