4

I'd love to have a more elegant solution for a mass rename of files, as shown below. Files were of format DEV_XYZ_TIMESTAMP.dat and we needed them as T-XYZ-TIMESTAMP.dat. In the end, I copied them all (to be on the same side) into renamed folder:

ls -l *dat|awk '{system("cp " $10 " renamed/T-" substr($10, index($10, "_")+1))}'

So, first I listed all dat files, then picked up 10th column (file name) and executed a command using awk's system function. The command was essentially copying of original filename into renamed folder with new file name. New file name was created by removing (awk substring function) prefix before (including) _ and adding "T-" prefix.

Effectively:

cp DEV_file.dat renamed/T-file.dat

Is there a way to use cp or mv together with some regex rules to achieve the same in a bit more elegant way?

Thx

hummingBird
  • 2,495
  • 3
  • 23
  • 43
  • 2
    http://mywiki.wooledge.org/ParsingLs – stark Apr 03 '20 at 11:45
  • 2
    Awk is a tool for manipulating text. Shell is a tool for manipulating files/processes and sequencing calls to tools. You're trying to use awk to manipulate (rename) files so therefore you're using the wrong tool - just use shell. – Ed Morton Apr 03 '20 at 12:00

3 Answers3

4

You may use this script:

for file in *.dat; do
   f="${file//_/-}"
   mv "$file" renamed/T-"${f#*-}"
done

You must avoid parsing output of ls command.

anubhava
  • 761,203
  • 64
  • 569
  • 643
  • OK, I have to admit `f="${file//_/-}"` and `"${f#*-}"` syntax is a total new for me. Thanks. – hummingBird Apr 03 '20 at 11:23
  • 1
    Yeah, but be sure to add `#!/bin/bash` shebang on the first line because if `sh` gets its hands on the script, it will complain. – Quasímodo Apr 03 '20 at 11:25
  • 2
    @Quasímodo: Question is tagged as `bash` as well. – anubhava Apr 03 '20 at 11:28
  • 1
    @hummingBird: `f="${file//_/-}"` replaces all `_` with `-` in `$file` and stores result in variable `f`. `"${f#*-}"` removes test until first `-` in `$f`. – anubhava Apr 03 '20 at 11:29
  • 2
    @anubhava Sure, that is a totally valid answer, it was just a reminder for OP. – Quasímodo Apr 03 '20 at 11:30
  • Thx both for your great answers. I accepted Quasimodo's for the clear fact it came in faster and it solved my problem. – hummingBird Apr 04 '20 at 22:16
  • 1
    Just to let you know I had posted my answer 6 minutes before accepted answer. Also in terms of efficiency it is always faster to avoid spawning subprocesses and calling external utilities if something can be entirely done in bash itself. Quasimodo's forks 2 subprocesses: one for command substitution and two for pipe inside `$9...)`. – anubhava Apr 05 '20 at 08:00
2

If you have rename utilitity

rename -E "s/[^_]*/T/" -e "s/_/-/g" *dat

Demo

$ls -1
ABC_DEF_TIMESTAMP.dat
DEV_XYZ_TIMESTAMP.dat
$rename -E  "s/[^_]*/T/" -e "s/_/-/g" * 
$ls -1
T-DEF-TIMESTAMP.dat
T-XYZ-TIMESTAMP.dat
$
Digvijay S
  • 2,665
  • 1
  • 9
  • 21
  • 2
    Please note that there are different rename utilities. Depending on your system this command might have a different name, for instance `perl-rename` on arch. See https://unix.stackexchange.com/q/229230/187122. – Socowi Apr 03 '20 at 14:46
1

This is how I would do it:

cpdir=renamed
for file in *dat; do
    newfile=$(echo "$file" | sed -e "s/[^_]*/T/" -e "y/_/-/")
    cp "$file" "$cpdir/$newfile"
done

The sed scripts transforms every non-underscore leading characters in a single T and then replaces every _ with -. If cpdir is not sure to exist before execution, you can simply add mkdir "$cpdir" after first line.

Quasímodo
  • 3,812
  • 14
  • 25