1

I'm attempting to recursively remove a leading underscore from some scss files. Here's what I have.

find . -name '*.scss' -print0 | xargs -0 -n1 bash -c 'mv "$0" `echo $0 | sed -e 's:^_*::'`'

When I'm in a specific directory this works perfectly:

for FILE in *.scss; do mv $FILE `echo $FILE | sed -e 's:^_*::'`; done

What am I doing wrong in the find?

oguz ismail
  • 1
  • 16
  • 47
  • 69

3 Answers3

1

As the starting point is ., all paths that find prints start with a dot. Thus ^_* doesn't match anything and sed returns its input unchanged.

I wouldn't bother with sed or xargs though.

The script below works with any find and sh that isn't terribly broken, and properly handles filenames with underscores in the middle as well.

find . -name '_*.scss' -exec sh -c '
for fp; do                # pathname
  fn=${fp##*/}            # filename
  fn=${fn#"${fn%%[!_]*}"} # filename w/o leading underscore(s)
  echo mv "$fp" "${fp%/*}/$fn"
done' sh {} +

A less portable but shorter and much cleaner alternative in bash looks like:

shopt -s globstar extglob nullglob

for fp in ./**/_*.scss; do
  echo mv "$fp" "${fp%/*}/${fp##*/+(_)}"
done

Drop echo if the output looks good.

oguz ismail
  • 1
  • 16
  • 47
  • 69
0

Look at the syntax highlighting of the sed command. Single quotes can't be nested. Easiest fix: switch to double quotes.

find . -name '*.scss' -print0 | xargs -0 -n1 bash -c 'mv "$0" `echo $0 | sed -e "s:^_*::"`'

I would recommend leaving $0 as the program name and using $1 for the first argument. That way if bash prints an error message it'll prefix it with bash:.

find . -name '*.scss' -print0 | xargs -0 -n1 bash -c 'mv "$1" `echo "$1" | sed -e "s:^_*::"`' bash

You can also simplify this with find -exec.

find . -name '*.scss' -exec bash -c 'mv "$1" `echo "$1" | sed -e "s:^_*::"`' bash {} ';'

You could also let bash do the substitution with its ${var#prefix} prefix removal syntax:

find . -name '*.scss' -exec bash -c 'mv "$1" "${1#_}"' bash {} ';'
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
0

You don't need find at all (unless you are trying to do this with an ancient version of bash, such as what macOS ships):

shopt -s globstar extglob

for f in **/*.scss; do
    mv -- "$f" "${f##*(_)}"
done

${f##...} expands f, minus the longest prefix matching .... The extended pattern *(_) matches 0 or more _, analogous to the regular expression _*.

chepner
  • 497,756
  • 71
  • 530
  • 681