Reputation: 3556
I have files that are named like this:
A-1230.pdf
A-2450.pdf
A-6780.pdf
B-1230.pdf
B-2450.pdf
B-6780.pdf
... and a directory-structure like this:
A
- 000
- 001
-...
- 999
B
- 000
- 001
-...
- 999
No I want to move the files to their corresponding subdirectories. Means
A-1230.pdf goes into A -> 123
B-2450.pdf goes into B -> 245
And so on.
I tried the following with bash:
mv +([A-Z]-[0-9][0-9][0-9]*.pdf) $1/$2$3$4
But that's not working. How do I use backreferences and capturing groups in bash correctly?
Thanks for any help!
Upvotes: 0
Views: 87
Reputation: 26471
What you are attempting to achieve is what is called back-referencing. Unfortunately, there is no such concept for globular expressions. Expressions such as:
mv (*).pdf $1/
cp ([0-9])-(*).txt $2-$1.text
are simply not possible in any shell (bash, zsh, ksh, csh).
Workarounds exist by looping over every file and post process it a bit:
for file in [A-Z]-[0-9][0-9][0-9]*.pdf; do
dir="${file:0:5}"; dir="${dir/-/\/}"
mkdir -p "$dir"
mv "$file" "$dir"
done
or a bit more intelligently as is done by Charles Duffy
ZSH developed, for tasks like this, a set of separate tools (zcp
, zmv
and zln
) which allow back referencing. So your task could be performed as:
zmv -n '([A-Z])-([0-9][0-9][0-9])[0-9].pdf' '$1/$2/'
The -n
flag shows what it will do but not actually perform the action as it acts as a dry-run. remove the flag to perform the actual moves.
note: ZSH does not have back-referencing for globular expressions. Notice the single quotes used in the zmv
example above. They prohibit globbing. The FROM and DEST part are arguments past to those tools. The tools are also not found in any $PATH
as they are part of the zsh system. This in contrast to cp
, mv
and ln
which are actual binaries.
Upvotes: 0
Reputation: 295288
Regex support in bash is used with [[ $string =~ $regex ]]
, and places capture groups in the array BASH_REMATCH
.
for file in ?-*.pdf; do
[[ $file =~ ([[:alpha:]])-([[:digit:]]{3}).* ]] || continue
mkdir -p -- "${BASH_REMATCH[1]}/${BASH_REMATCH[2]}" || continue
mv -- "$file" "${BASH_REMATCH[1]}/${BASH_REMATCH[2]}/"
done
Upvotes: 3