tunawolf
tunawolf

Reputation: 63

Cut string from beginning of file and append to filename in Bash

I want to cut a certain length of string from the start of my file and append it to my filename. Is there a one-liner that can do this for a whole directory of files?

For example, I have the file 1.txt, with the following contents:

123456abcdefg12345678...

I want to rename the file to 1_123456.txt, and edit the contents to:

abcdefg12345678....

I have created a bash script to solve this problem, like so:

for f in ./*; do
  a = $(head -c 8 $f)
  cut -c 9- > $f
  mv {,$a_}$f
done

But this doesn't do what it's supposed to do- the problem seems to be in the mv line. I would also appreciate it if a one-liner could also do the trick.

Upvotes: 0

Views: 1419

Answers (2)

William Pursell
William Pursell

Reputation: 212268

$ echo 123456abcdefg12345678 > 1.txt
$ tail -c +7 1.txt > 1_$(head -c +6 1.txt).txt ; rm 1.txt . 
$ cat 1_123456.txt 
abcdefg12345678

Upvotes: 1

GMc
GMc

Reputation: 1774

Maybe not a one liner (unless you put it into a shell script) but you could try this:

#!/bin/bash

# require two parameters:
# 1 the name of the input directory and
# 2 the name of the output directory
if [ $# -ne 2 ]
then
  echo usage
  exit 1
fi

INDIR=$1
OUTDIR=$2
CHAR_CNT=4                         # Number of chars to take from file content and insert into output file name

# Create the output directory just in case it does not exist.
mkdir -p $OUTDIR

# for each file.
for FILENAME in $INDIR/*
do
  if [ -f $FILENAME ]              # Make sure it is a regular file.
  then
    BASENAME=`basename $FILENAME`  # Extract just the file name.

    NAMEPART=${BASENAME%.*}        # From the file name, get just the name part.
    EXT=${BASENAME##*.}            # From the file name, get just the extension.
    PREFIX=`head -1 $FILENAME | cut -c 1-$CHAR_CNT`    # Get the first n characters from the input file.
    NEWFILE=$OUTDIR/${NAMEPART}_$PREFIX.$EXT   # Construct the output file name (and output path)

    # Degugging statements to show what is going on.
    #echo file: $FILENAME - NamePart: $NAMEPART, extension: $EXT, prefix = $PREFIX
    #echo new file name: $NEWFILE

    # Transfer the input file to the output, remove the first n characters from the file.
    # The output file will be named as per the format originalName_charsFromFile.extension
    sed "1s/^.\{$CHAR_CNT\}//" < $FILENAME > $NEWFILE
    echo "processed: $FILENAME -> $NEWFILE"
  else                             # Not a regular file.
    echo skipping $FILENAME
  fi
done

I put the above into a script called renameSpecial.sh. When I run it, I get the result shown below.

gmc@linux-ihon:~/projects/so/renFil>   #### Starting point - input files
gmc@linux-ihon:~/projects/so/renFil> tree .
.
└── sampleData
    ├── file1.txt
    ├── file2.txt
    ├── testdir
    │   └── ignoredFile.txt
    ├── t.txt
    └── x.y.z.txt

2 directories, 5 files
gmc@linux-ihon:~/projects/so/renFil> cat sampleData/x.y.z.txt 
1234567890abcdefg
gmc@linux-ihon:~/projects/so/renFil> cat sampleData/t.txt 
12aaaa
34bbb
56cccc
gmc@linux-ihon:~/projects/so/renFil> cat sampleData/file2.txt 
abcdefg
gmc@linux-ihon:~/projects/so/renFil> cat sampleData/file1.txt
aaaaaaa
gmc@linux-ihon:~/projects/so/renFil> 
gmc@linux-ihon:~/projects/so/renFil> 
gmc@linux-ihon:~/projects/so/renFil>  #### Run the command
gmc@linux-ihon:~/projects/so/renFil> renameSpecial.sh 
usage: /home/glennm/bin/renameSpecial.sh inDir outDir
where:
  inDir    the name of a directory containing the input files
  outDir   the name of a directory that will contain the output files
           if outDir does not exist, this script will create it.

gmc@linux-ihon:~/projects/so/renFil> 
gmc@linux-ihon:~/projects/so/renFil> renameSpecial.sh sampleData outData
processed: sampleData/file1.txt -> outData/file1_aaaa.txt
processed: sampleData/file2.txt -> outData/file2_abcd.txt
skipping sampleData/testdir
processed: sampleData/t.txt -> outData/t_12aa.txt
processed: sampleData/x.y.z.txt -> outData/x.y.z_1234.txt
gmc@linux-ihon:~/projects/so/renFil> 
gmc@linux-ihon:~/projects/so/renFil> #### Result new directory with *new* file names
gmc@linux-ihon:~/projects/so/renFil> 
gmc@linux-ihon:~/projects/so/renFil> tree .
.
├── outData
│   ├── file1_aaaa.txt
│   ├── file2_abcd.txt
│   ├── t_12aa.txt
│   └── x.y.z_1234.txt
└── sampleData
    ├── file1.txt
    ├── file2.txt
    ├── testdir
    │   └── ignoredFile.txt
    ├── t.txt
    └── x.y.z.txt

3 directories, 9 files
gmc@linux-ihon:~/projects/so/renFil> 
gmc@linux-ihon:~/projects/so/renFil> ### Finally, the chars used in the file names are removed from the beginning of the file.
gmc@linux-ihon:~/projects/so/renFil> 
gmc@linux-ihon:~/projects/so/renFil> cat outData/x.y.z_1234.txt 
567890abcdefg
gmc@linux-ihon:~/projects/so/renFil> cat outData/t_12aa.txt 
aa
34bbb
56cccc
gmc@linux-ihon:~/projects/so/renFil> 

If you want to "rename" the files, then simply add an rm $FILENAME after the sed step near the bottom of the if statement.

You say that you want to rename the files, my example creates a new version of the processed files in a new location. This could still meet your needs, just rename your input directory before you start, or copy the output files back to the input directory (e.g. as a final step). I just did it this way as it aids debugging, I can run it as many times as I like without having to recreate my inputs and so on.

Upvotes: 0

Related Questions