2

I have this bash script

 #!/bin/bash
 find . -type f > /home/wschrabi/filenames
 while read filename; do
       stripped="$(printf '%s\n' "$filename" | tr -d -C '[[:alnum:]][[:space:]][!\"\#\$\%\&\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_`{|}~]')";
       ohne="$(sed -e 's/[\d126-\d255\"*:<>\?\\\|]/_/g' <<<$filename)";
       test "$filename" = "$stripped" || printf "mv '%s' '%s'\n " "${filename//[\']/'\\''}" "${ohne//[\'\"\`]/_}"; 
 done < /home/wschrabi/filenames

and at the part ${filename//[\']/'\\''} I would like to substitute a single quote in the filename. The purpose should be that when I pipe the output in a bash script I would like to rename all filenames automatically. But the single quotes make problems.

Thanks a lot for any advices. Walter

EDIT: Here is for completion my chekc NTFS for Filename script.

     #!/bin/bash
     #
     # Quote a string, wrapping it in tick marks ('), and escaping any
     # embedded ticks.
     #
     # Inputs:
     #   $1: The string to quote
     #
     # Returns:
     #   (stdout) - The quoted string
     #
     quote() {
         if [ $(echo "$@" | tr -d "\n" | wc -c) -eq 0 ]; then
             echo "''"
         elif [ $(echo "$@" | tr -d "[a-z][A-Z][0-9]:,.=~_/\n-" | wc -c) -gt 0 ]; then
             echo "$@" | sed -e "s/'/\'\"\'\"\'/g" | sed -e "s/^/'/g" -e "s/$/'/g"
         else
             echo "$@"
         fi
     }

     if [ $# -eq 0 ]
       then echo "No arguments supplied: USAGE: checkNTFS_filenames.sh <file_to_check>" 
     fi

     if [ $# -eq 1 ]
     then

     #find . -type f > /home/wschrabi/filenames2
     while read filename; do
           stripped="$(printf '%s\n' "$filename" | tr -d  '\"\*\:\<\>\?\\\|')";
           ohne="$(sed -e 's/[\d126-\d255\"*:<>\?\\\|]/_/g' <<<$filename)";
           if [ "$filename" != "$stripped" ]  ;
           then  printf "mv %s %s\n" "$(quote "$filename")" "$(quote "$stripped")" 
           fi
     done < $1
     fi
1
  • I have added another answer, not to detract from virtex's, which is correct, but to point out there shorter, more efficient ways to do this., that's all. Nov 18, 2016 at 10:11

2 Answers 2

1

If you put your filenames between tick marks, escaping characters with a backslash won't work. There is another way to do it though. Here's a quote function I've used in the past that might be useful to you.

#
# Quote a string, wrapping it in tick marks ('), and escaping any
# embedded ticks.
#
# Inputs:
#   $1: The string to quote
#
# Returns:
#   (stdout) - The quoted string
#
quote() {
    if [ $(echo "$@" | tr -d "\n" | wc -c) -eq 0 ]; then
        echo "''"
    elif [ $(echo "$@" | tr -d "[a-z][A-Z][0-9]:,.=~_/\n-" | wc -c) -gt 0 ]; then
        echo "$@" | sed -e "s/'/\'\"\'\"\'/g" | sed -e "s/^/'/g" -e "s/$/'/g"
    else
        echo "$@"
    fi
}

When you run it you get outputs like these.

$ quote abc
abc
$ quote "abc'def"
'abc'"'"'def'
$ quote \"abc\"
'"abc"'
$ quote "abc def"
'abc def'

The idea here is that you can replace the final printf in your script with this:

printf "mv %s %s\n" "$(quote "$filename")" "$(quote "$ohne")"

Using a filename of abc'def with your script with the above change I got this as my output.

mv 'abc'"'"'def' abcdef

and that successfully renamed the file when executed.

0
0

Virtex answer is of course perfectly correct. I would just like to point out that this kind of operation is easier to carry out with Bash arrays: just to make it clear, let us assume we wish to strip the initial characters ./ which find leaves on the file names in this simple command:

find -maxdepth 1 -type f

This can be done as follows:

#!/bin/bash

declare -a mylist
mylist=(`find -maxdepth 1 -type f `)
mylist=(${mylist[@]/\.\//})
echo "${mylist[@]}"

Bash search and replace works on arrays as one would hope: by modifying the array elements one by one. This can be modified to include your example.

EDIT:

what if I have millions of files?

How about 10 millions?

$ time /bin/bash -c 'unset array &&  declare -a array && array=($(seq 1 10000000)) && echo "${array[6]}"; unset array'
7

real    0m11.110s
user    0m10.412s
sys     0m0.724s

$ inxi -C
CPU:       Dual core Intel Core i7-5500U (-HT-MCP-) cache: 4096 KB 
           clock speeds: max: 3000 MHz 1: 2509 MHz 2: 2471 MHz 3: 2631 MHz 4: 2471 MHz
2
  • Thanks Marius, but what if I have millions of files? Would there a RAM memory limit for the bash arrays? Nov 18, 2016 at 10:15
  • @WalterSchrabmair Pls see my Edit. Nov 18, 2016 at 10:33

You must log in to answer this question.

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