Search code examples
bashshellrenamemv

Remove middle of name in bash


I have 300+ files named:

T1_0000106_FS1_MAX_5743.nii.gz  T1_0000214_FS1_MAX_5475.nii.gz
T1_0000107_FS1_MAX_5477.nii.gz  T1_0000215_FS1_MAX_6162.nii.gz

I would like to remove everything between T1 and _5/6*.nii.gz so:

T1_5743.nii.gz  T1_5475.nii.gz
T1_5477.nii.gz  T1_6162.nii.gz 

I can't figure out why it isn't working; I tried (from another post):

 for file in *.gz;
 do new_name=$(sed 's/_[^.]*_/_g' <<< "new_name");
 mv "$file" "$new_name"; done

and variations of rename/sed but nothing changes.


Solution

  • Problems with your script include, at least,

    • s/_[^.]*_/_g is not a valid sed command. You appear to want s/_[^.]*_/_/g, or in this case, s/_[^.]*_/_/ would do fine as well.

    • <<< "new_name" redirects the literal string new_name into sed. Possibly you mean <<< "$new_name"

    Personally, though, I would not bother with sed for this job, especially if you have a large number of files. Bash is perfectly capable of doing fairly complex string manipulation itself, and your needs don't push it too hard. Consider:

    for f in *.gz; do
        # The value of $f with the longest trailing match to _* removed
        head=${f%%_*}
    
        # The value of $f with the longest leading match to *_ removed
        tail=${f##*_}
    
        new_name="${head}_${tail}"
    
        # Sanity check and avoid needless errors
        if [ "$f" != "$new_name" ]; then
            mv "$f" "$new_name"
        fi
    done