0

I know this topic has been already covered (somehow here and also somewhere else) but I beg you indulgence because I'm trying to find my way through the bash scripting art;

I need to sort out the following problem

given that I have:

dir1

  • file 1
  • file 2
  • and so on...

dir2

  • file1
  • file2
  • and so on...

I want to rename files so that

dir1

  • dir1_file 1
  • dir1_file 2
  • dir1_and so on...

dir2

  • dir2_file1
  • dir2_file2
  • dir2_and so on...

so far I've managed to work out the following "exploratory" bash script

for d in `ls -d */`
 do
   #echo "$d"
   pre="${d:0:-1}"
   #echo "$pre"
   #cd "${d}"
   pushd "${d}"
    for f in *.jp2
     do
        #echo "${f}"
        echo "${pre}_${f}"
     done
   #cd - 
   #popd    
 done

it's seems working (quite) fine except that of the error message

line 9: pushd: dir2/: No such file or directory

where dir2 is the name of last dir being swept by the for loop...

any help for that? what I'm missing?

2 Answers 2

0

Rather than trying to cd back and forth into directories (popd and pushd are primarily for interactive use anyway):

#!/bin/sh

for name in */*; do
    [ ! -f "$name" ] && continue

    dir="$( basename "$( dirname "$name" )" )"
    newname="$dir/${dir}_${name##*/}"

    if [ ! -e "$newname" ]; then
        echo mv "$name" "$newname"
    fi
done

This iterates over all names in all directories in the current directory. It will skip non-files and extract the directory basename (the name of the directory with no path) into dir. It then constructs a new filename, $newname, for the file using $dir and the basename portion of the pathname in $name (${name##*/} may be replaced by $( basename "$name" )).

If the new name is not already taken, the file is renamed.

Remove the echo to actually rename the files once you have verified that it ought to do the right thing.


To perform a set of actions in another directory and then come back, use

( cd dir
  action
  action 
  action )

The changed directory does not influence the environment outside of the ( ... ).


See also: Why *not* parse `ls`?

2
  • I see you mean something like this... for d in ls -d */ do pre="${d:0:-1}" (cd "${d}" for f in *.jp2 do echo "${pre}_${f}" done ) done
    – maxbre
    Commented Oct 6, 2017 at 10:27
  • I'll carefully study your more efficient code, thanks
    – maxbre
    Commented Oct 6, 2017 at 10:32
0

You say “dir2 is the name of last dir being swept by the for loop”, but it’s actually the second and last, isn’t it?  It’s failing because you have the popd commented out.  At the end of the first loop iteration, you’re still “in” dir1, so your attempt to pushd dir2 is actually looking for dir1/dir2; i.e., dir2 within dir1.

If you have seven directories, it would fail for the last six.

You raise the concern that “uncommenting popd exposes to other unwanted aftermaths, .... i.e., what about the case if a file (not a dir) is present among the dirs (at the same level) being iterated by for loop”.  Well,

  • That logic is like saying, “If I wear a right shoe in addition to a left shoe, that gives me one more thing to aim my gun at, so I’m all the more likely to shoot myself in the right foot.”  This makes no sense.  Doing pushd and not following it with popd (especially in a loop) is much, much more wrong than wearing only one shoe.
  • Since you’re using */, you will get only directories.
    • But Kusalananda is right: you should use for d in */ rather than for d in `ls -d */`⁠.
  • Better yet, you could do something like

    for d in *
     do
        …
        if [ -d "$d" ]
        then
            pushd "$d"
            for f in *.jp2
             do
                …
             done
            popd    
        fi
     done
    

    or

    for d in *
     do
        …
        if pushd "$d" 2> /dev/null
        then
            for f in *.jp2
             do
                …
             done
            popd    
        fi
     done
    

    so you don’t try to treat a non-directory as a directory.

3
  • yes, you are right; let me just say that by uncommenting popd exposes to other unwanted aftermaths, at least I think to understand.... i.e. what about the case if a file (not a dir) is present among the dirs (at the same level) being iterated by for loop...
    – maxbre
    Commented Oct 6, 2017 at 10:20
  • both your amended solutions (for not shooting in the right foot!) do not seem to work properly, as far as I understand... i.e. the same problem of treating a non-directory as a directory I'll stay on the other line... thanks anyway
    – maxbre
    Commented Oct 6, 2017 at 11:29
  • The second version did have an issue of producing many error messages — I have fixed that. Otherwise, they both work for me. If you can clarify what is going wrong, I might be able to help you. For example, are you getting to the for f in *.jp2 statement when you haven’t pushd’d into a subdirectory? Are you doing popd when you aren’t pushd’d into a subdirectory? Commented Oct 6, 2017 at 19:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .