1

I have a directory called assets and it contains a file structure similar to this:

├── a
│   ├── b
│   │   ├── c.png
│   │   └── d.png
│   └── b2
│       ├── e.png
│       ├── f.png
│       └── g.png
└── a2
    └── b3
        └── h.png

How can I change the name of all the images in this structure to the pattern NAME_old.png where NAME is the original name of the file? (For example, c.png would be changed to c_old.png)

I have a feeling that it has something to do with sed/awk/grep and using pipes, but I haven't the faintest idea how to use these tools and reading documentation doesn't really help.

jesse_b
  • 37,005

3 Answers3

4

Using find

find assets -type d -exec sh -c '
    for n in "$1"/*.png; do
        test -f "$n" && mv "$n" "${n%.png}_old.png"
    done' sh {} ';'

This find command looks for directories in or under the assets directory. For each found directory, it executes the following short shell script:

for n in "$1"/*.png; do
    test -f "$n" && mv "$n" "${n%.png}_old.png"
done

where $1 will expand to the path to the found directory. The script will iterate over all filenames matching *.png in the directory and replace the .png filename suffix with _old.png using a variable substitution.

The test -f makes sure that we're operating on existing files. This is needed because if a directory dose not contain any files matching *.png (as in the assets, a and a2 directories), then that pattern will remain unexpanded in $n.

Alternatively, and shorter,

find assets -type f -name '*.png' \
    -exec sh -c 'for n; do mv "$n" "${n%.png}_old.png"; done' sh {} +

Testing:

$ mkdir -p assets/a{,2}/b{,2,3}
$ touch assets/a{,2}/b{,2,3}/{a,b,c,d}.png
$ tree
.
`-- assets
    |-- a
    |   |-- b
    |   |   |-- a.png
    |   |   |-- b.png
    |   |   |-- c.png
    |   |   `-- d.png
    |   |-- b2
    |   |   |-- a.png
    |   |   |-- b.png
    |   |   |-- c.png
    |   |   `-- d.png
    |   `-- b3
    |       |-- a.png
    |       |-- b.png
    |       |-- c.png
    |       `-- d.png
    `-- a2
        |-- b
        |   |-- a.png
        |   |-- b.png
        |   |-- c.png
        |   `-- d.png
        |-- b2
        |   |-- a.png
        |   |-- b.png
        |   |-- c.png
        |   `-- d.png
        `-- b3
            |-- a.png
            |-- b.png
            |-- c.png
            `-- d.png

9 directories, 24 files

(find command executed here)

$ tree
.
`-- assets
    |-- a
    |   |-- b
    |   |   |-- a_old.png
    |   |   |-- b_old.png
    |   |   |-- c_old.png
    |   |   `-- d_old.png
    |   |-- b2
    |   |   |-- a_old.png
    |   |   |-- b_old.png
    |   |   |-- c_old.png
    |   |   `-- d_old.png
    |   `-- b3
    |       |-- a_old.png
    |       |-- b_old.png
    |       |-- c_old.png
    |       `-- d_old.png
    `-- a2
        |-- b
        |   |-- a_old.png
        |   |-- b_old.png
        |   |-- c_old.png
        |   `-- d_old.png
        |-- b2
        |   |-- a_old.png
        |   |-- b_old.png
        |   |-- c_old.png
        |   `-- d_old.png
        `-- b3
            |-- a_old.png
            |-- b_old.png
            |-- c_old.png
            `-- d_old.png

9 directories, 24 files
Kusalananda
  • 333,661
  • Why spawn a shell for each directory? Why not just use find assets -type f -name '*.png' -exec sh -c 'for f do mv "$f" "${f%.png}_old.png"; done' sh {} +? – Wildcard Feb 13 '18 at 21:53
  • @Wildcard You mean for each file? (I'm assuming you are referring to the second find). Yes, I have updated that now. And thanks! – Kusalananda Feb 14 '18 at 06:59
3

Use prename together with find:

find . -type f -name '*.png' -exec prename 's/\.png$/_old.png/' {} +
αғsнιη
  • 41,407
Barun
  • 2,376
1

Without find

Given the fixed directory structure, you can use wildcards for directories:

rename -n s/.png/_old.png/ */*/*.png

(change -n to -v to actually run the command).

If the PNG's are at different directory levels, you can still avoid find:

shopt -s globstar
rename -n s/.png/_old.png/ **/*.png

(with globstar set, ** matches any number of directory levels)

On Debian and derivatives, prename is called rename, but it's the same command.

xenoid
  • 8,888