0

I am new to Unix scripting.

I want to rename a file based on a matching string in the first line (i.e. line starting with HDR).

I have a text file (File.txt) which looks like below:

HDR##############################1234###
########################################
########################################

Below is my code. How can I be more specific in my code to check only the first line in the file, which starts with HDR and has the pattern 1234 or 5678?

if grep -o "1234" File.txt

then mv File.txt Pattern1.txt

echo "File with pattern1 received..."

elif grep -o "5678" File.txt

then mv File.txt Pattern2.txt

echo "File with pattern2 received..."

else

echo "File have no matching pattern..."

fi
2
  • Do you only want to test the first line for HDR and the number, or do you want to test the first line that starts with HDR for the number?
    – Kusalananda
    Jun 13 at 6:25
  • I wanted to test the first line that stars with HDR for the number.
    – Sudha
    Jun 14 at 10:27

4 Answers 4

1

Read a line, using "read", then use "case" to decide what to do.

{
  IFS= read -r Firstline
  case "$Firstline" in
     ("HDR"*"1234"*) mv File.txt Pattern1.txt
                echo "File with pattern1 received..." ;;
     ("HDR"*"5678"*) mv File.txt Pattern2.txt
                echo "File with pattern2 received..." ;;
     (*)        echo "Nothing matched" ;;
  esac
} < File.txt
2
  • Thank you @icaurus, it is working perfectly fine.
    – Sudha
    Jun 14 at 10:26
  • Great edit, thanks Stéphane
    – icarus
    Jun 15 at 17:25
0

Use the -n (--line-number) option of grep to determine the line, and then case to match the pattern:

Tmp=$(grep -n "HDR.*[0-9]\{4\}" File.txt)
if [ "${Tmp%:*}" -eq 1 ]
  then  case "${Tmp#*:}" in
          HDR*1234*)    NewName="Pattern1";;
          HDR*5678*)    NewName="Pattern2";;
        esac
        if [ "$NewName" ]
          then  mv -- File.txt "$NewName".txt
                echo "File with $NewName received..."
          else  echo "File doesn't have a matching pattern..."
        fi
  else  echo "File doesn't have a pattern in line 1"
fi
1
  • Thank you @RudiC, this piece of code doing renaming the file as expected.
    – Sudha
    Jun 14 at 10:29
0

Using the perl rename utility.

Note: perl rename is also known as file-rename, perl-rename, or prename. It is not to be confused with the rename utility from util-linux which has completely different and incompatible capabilities and command-line options.

$ rename -n ' BEGIN {
      # This block runs only once when the script starts, there's no need
      # to redefine these vars on every pass through the loop.  File::Rename
      # scripts run with `use strict vars`, so we need to be careful about
      # variable scope.  See `perldoc -f our`
      our %patterns=(1234 => "Pattern1.txt", 5678 => "Pattern2.txt");
      our $re = "^HDR#+(" . join("|",keys %patterns) . ")#+$";
    };
    
    # The remainder of the script runs once for every filename
    our (%patterns, $re); # these vars are in File::Rename lexical scope

    open(my $fh,"<",$_); my $line=<$fh>; close($fh);
    if ($line =~ /$re/) { $_ = $patterns{$1} }' File*
rename(File1.txt, Pattern1.txt)
rename(File2.txt, Pattern2.txt)

(this was run on two text files, copies of your sample data. File1.txt contain 1234 exactly the same as your File.txt example, while File2.txt was edited to contain 5678 instead).

The -n option makes it a dry run, so it will only show what it would do without actually renaming any files. Remove the -n, or replace it with -v for verbose output, when you've confirmed that it does what you want.

This rename script uses a hash %patterns to hold the patterns to search for AND the filenames to rename that files containing that pattern should be renamed to. It constructs a regular expression from the keys of the hash in variable $re.

Then it opens the current filename, and reads in the first line. If that first line matches one of the patterns, it is renamed to the corresponding filename.

Using a hash like this allows the script to be easily extended with more or different patterns. Apart from the %patterns hash (and the ^HDR#+( and )#+$ in $re), nothing is hard-coded.

NOTE: rename will not overwrite an existing file unless you force it to with the -f or --force option. This applies to existing files and to just-renamed files - if more than one file contains the same pattern, only the first will be renamed. Using -f will, of course, overwrite any existing/previously-renamed files - a better alternative would be to use a counter variable (e.g. a hash with the filenames or patterns as keys) to renamed the files to, e.g., Pattern1.txt.001, Pattern1.txt.002, etc.

Or with the help of the fileparse() function from the File::Basename module to Pattern1-001.txt, Pattern1-002.txt, etc.

e.g.

$ rename -n 'BEGIN {
    use File::Basename;
    our %seen=();
    our %patterns=(1234 => "Pattern1.txt", 5678 => "Pattern2.txt");
    our $re = "^HDR#*(" . join("|",keys %patterns) . ")"
  };

  our(%seen, %patterns, $re);
  open(my $fh,"<",$_); my $line=<$fh>; close($fh);
  if ($line =~ /$re/) {
    my($name,$path,$suffix) = fileparse($patterns{$1}, qr/\.[^.]*$/);
    $_ = sprintf "%s-%03i%s", $name, ++$seen{$1}, $suffix
  }' File*
rename(File1.txt, Pattern1-001.txt)
rename(File2.txt, Pattern2-001.txt)
rename(File3.txt, Pattern1-002.txt)
-2
cl=$(awk 'NR==1 /^HDR/ && /1234|5678/' filename|wc -l)
if [[ $cl -gt 0 ]]
then
echo "Pattern matched"
mv <filename> <newfilename>
else
echo "No pattern matched"
fi
1
  • The question calls for distinguishing between 1234 and 5678. Jun 13 at 9:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.