Radek
Radek

Reputation: 3941

Strip leading dot from filenames bash script

I have some files in a bunch of directories that have a leading dot and thus are hidden. I would like to revert that and strip the leading dot.

I was unsuccessful with the following:

for file in `find files/ -type f`;
do
base=`basename $file`
if [ `$base | cut -c1-2` = "." ];
then newname=`$base | cut -c2-`;
dirs=`dirname $file`;
echo $dirs/$newname;
fi
done

Which fails on the condition statement:

[: =: unary operator expected

Furthermore, some files have a space in them and file returns them split.

Any help would be appreciated.

Upvotes: 16

Views: 21579

Answers (3)

SiegeX
SiegeX

Reputation: 140427

  1. This script works with any file you can throw at it, even if they have spaces, newlines or other nefarious characters in their name.
  2. It works no matter how many subdirectories deep the hidden file is
  3. Unlike other answers thus far, you don't have to change the rest of the script when you change the path given to find

*Note: I included an echo so that you can test it like a dry-run. Remove the single echo if you are satisfied with the results.

find . -name '.*' -exec sh -c 'for arg; do d="${arg%/*}"; f=${arg:${#d}}; echo mv "$arg" "$d/${f#.}"; done' _ {} +

Upvotes: 1

John Kugelman
John Kugelman

Reputation: 361927

The easiest way to delete something from the start of a variable is to use ${var#pattern}.

$ FILENAME=.bashrc;    echo "${FILENAME#.}"
bashrc
$ FILENAME=/etc/fstab; echo "${FILENAME#.}"
/etc/fstab

See the bash man page:

${parameter#word}
${parameter##word}

The word is expanded to produce a pattern just as in pathname expansion. If the pattern matches the beginning of the value of parameter, then the result of the expansion is the expanded value of parameter with the shortest matching pattern (the ‘‘#’’ case) or the longest matching pattern (the ‘‘##’’ case) deleted.

By the way, with a more selective find command you don't need to do all the hard work. You can have find only match files with a leading dot:

find files/ -type f -name '.*'

Throwing that all together, then:

find files/ -type f -name '.*' -printf '%P\0' |
    while read -d $'\0' path; do
        dir=$(dirname "$path")
        file=$(basename "$path")
        mv "$dir/$file" "$dir/${file#.}"
    done

Additional notes:

  1. To handle file names with spaces properly you need to quote variable names when you reference them. Write "$file" instead of just $file.

  2. For extra robustness the -printf '\0' and read -d $'\0' use NUL characters as delimiters so even file names with embedded newlines '\n' will work.

Upvotes: 36

Arnaud Le Blanc
Arnaud Le Blanc

Reputation: 99921

find files/ -name '.*' -printf '%f\n'|while read f; do
        mv "files/$f" "files/${f#.}"
done

Upvotes: 7

Related Questions