Using bash script, How to add a comment in the beginning of each file to all files in a folder - bash

How do i Add a comment in the beginning of each file with file name the folder name. Need to do this to all files in a folder
eg:
directory:Chapter01
Files: file1_1
Sample inpput:
cat file1_1
hello hello hello
Expected output:
cat file1_1
####Directory Name:Chapter01, File Name: file1_1
hello hello hello

This untested snippet should work.
for i in *
do
echo "Processing ${i}"
echo "Directory Name:$(pwd), File name:${i}" > ${i}.tmp
cat ${i} >> ${i}.tmp
echo "Created ${i}"
# mv ${i}.tmp ${i}
done
This will create a .tmp corresponding to each file in your directory. The commented out mv will change the original file. You can do that if you're happy with the results.

awk to the rescue!
$ awk 'FNR==1{n=split(FILENAME, a, "/");
print "# Dir name: " a[n-1] " File name: " a[n] > FILENAME}
{print > FILENAME}' path/to/files/*
as a side benefit, it will skip empty files. It will print the immediate parent directory "files" and the actual filename. You need to run at least one dir up.

Use an editor to change the current files.
dirname=${PWD##*/}
for file in inp*; do
ed -s "${file}" <<EOF >/dev/null
1
a
####Directory Name:${dirname}, File Name: ${file}
.
w
q
EOF
done

Related

How to create a bash script to make directories and specific files inside each directory

I wrote a bash script trying to generate one directory named after each file inside the directory from which I run the script.
Original directory= /home/agalvez/data//sims/phylip_format
sim1.phylip
sim2.phylip
Directories to create = sim1 sim2
The contents of these new directories should be a copy of the original file that names the new directory and an extra file called "input". This file should contain the name of the .phylip file as well as the following:
"Name of original file"
U
5
Y
/home/agalvez/data/sims/trees/tree_nodenames.txt
After that I want to run the following command (sequentially) in all these new directories:
phylip dollop < input > screenout
My approach is the following one but it is not working:
!/bin/bash
for f in *.phylip;
mkdir /home/agalvez/data/sims/dollop/$f;
cp $f /home/agalvez/data/sims/dollop/$f;
cd /home/agalvez/data/sims/dollop/$f;
echo "$f" | cat > input;
echo "U" | cat >> input;
echo "5" | cat >> input;
echo "Y" | cat >> input;
echo "/home/agalvez/data/sims/trees/tree_nodenames.txt" | cat >> input;
phylip dollop < input > screenout;
;done
Edit: The error messge looks like this:
line 4: syntax error near unexpected token `mkdir'
line 4: ` mkdir /home/agalvez/data/sims/dollop/$f;'
FINAL SOLUTION:
#!/bin/bash
for f in *.phylip;
do
mkdir /home/agalvez/data/sims/dollop/$f;
cp /home/agalvez/data/sims/phylip_format/$f /home/agalvez/data/sims/dollop/$f;
cd /home/agalvez/data/sims/dollop/$f;
echo "$f" | cat > input;
echo "U" | cat >> input;
echo "5" | cat >> input;
echo "Y" | cat >> input;
echo "/home/agalvez/data/sims/trees/tree_nodenames.txt" | cat >> input;
phylip dollop < input > screenout;
done
The immediate problem is that you are lacking a do at the beginning of the loop body; but you'll want to refactor this code to avoid hardcoding the directory structure etc.
The first line needs to start with literally the two characters # and ! in order to be a valid shebang.
Notice also When to wrap quotes around a shell variable?
The printf could be replaced with a here document; I like the compactness of printf here.
#!/bin/bash
for f in *.phylip; do
mkdir -p dollop/"$f"
cp "$f" dollop/"$f"
cd dollop/"$f"
printf "%s\n" "$f" "U" "5" "Y" \
"/home/agalvez/data/sims/trees/tree_nodenames.txt" |
phylip dollop > screenout
done
Going forward, try http://shellcheck.net/ for diagnosing many common beginner problems in shell scripts.
Assuming you have a directory named pingping in your ${HOME} folder with files 1.txt, 2.txt, 3.txt. You can accomplish that like this. Modify this code to suit your needs.
#! /bin/bash
working_directory="${HOME}/pingping/"
cd $working_directory
for f in *.txt
do
mkdir "${f%%.*}"
if [ -f "${f%%.*}.txt" ]
then
if [ -d "${f%%.*}" ]
then
cp ${f%%.*}.txt ${f%%.*}
echo "Done copying"
#phylip dollop < input > screenout
#echo "Succesfully ran the command
fi
else
echo "not found"
fi
done

Why am I getting this file error and a cat error even though I am passing the text file in the argument?

The program calls for us to read in the directory full of text files, parse data from those files into their respective attributes.
Then once the data is set, load a general template which has those attributes in the text.
I'm using a sed command to replace the specific attributes, only if the number of students is greater than 50. If so it runs the sed command and writes to a file, and into a directory.
But i am getting this error when I'm passing
test3.sh ./data assign4.template 12/16/2021 ./output
Error
cat: assign4.template: No such file or directory
test3.sh: line 62: output/MAT3103.crs: No such file or directory
The current file is MAT4353.crs
Now what I am thinking is that, for the file or directory error, it is looking in that folder and searching for a file named that
But Not entirely sure how to resolve that.
As for the cat: template error, I don't get that since I am passing the template in the terminal
As for the other paramaters being passed, the Date which is also substituted in the sed command, All output files should be written to the directory defined by the last argument. This directory may or may not already exist. Each file should be named by the course’s department code and number,and with the extension.warn
Here is the total code
#!/bin/bash
# checking if user has passed atleast four arguments are passed
if [ $# -ne 4 ]
then
echo "Atleast 4 argument should be passed"
exit 1
fi
# if output directory exits check
if [ -d output ]
then
# if output directory exists will get deleted
echo "output directory already exists. So removing its contents"
rm -f output/*
else
# output directory does not exist, so gets created here
echo "output directory does not exist. So creating a new directory"
mkdir output
fi
max_students=50
template=$2
dt=$3
cd $1
for i in *; do
echo The current file is ${i}
dept_code=$(awk 'NR==2
{print $1 ; exit}' $i)
echo $dept_code
dept_name=$(awk 'NR==2
{print $2 ; exit}' $i)
echo $dept_name
course_name=$(awk 'FNR==2' $i)
echo $course_name
course_sched=$(awk 'FNR==3' $i | awk '{print $1}')
course_sched=$(awk 'FNR==3' $i | awk '{print $1}')
echo $course_sched
course_start=$(awk 'FNR==3' $i | awk '{print $2}')
echo $course_start
course_end=$(awk 'FNR==3' $i | awk '{print $3}')
echo $course_end
credit_hours=$(awk 'FNR==4' $i)
echo $credit_hours
num_students=$(awk 'FNR==5' $i)
echo $num_students
# checking if number of students currently enrolled > max students
if (( $(echo "$num_students > $max_students" |bc -l) ))
then
# output filename creation
out_file=${i}
# using example Template and sed command to replace the variables
cat $template | sed -e "s/\[\\[\dept_code\]\]/$dept_code/" | sed -e "s/\[\\[\dept_name\]\]/$dept_name/" | sed -e "s|\[\[course_name\]\]|$course_name|" | sed -e "s|\[\[course_start\]\]|$$
fi
done
You define the variable as
template=$2
and since your second parameter is assign4.template, this is what the variable template is set to. Then you do a
cat $template
which is, first of all, unnecessary, since you can do an input redirection on sed instead, but most of all requires, that the file exists in your working directory. Since you have done before a
cd $1
it means that the file data/assign4.template does not exist. You have to create this file before you can use your script.
use single quotes in your positional arguments.
test3.sh './data' 'assign4.template' '12/16/2021' './output'
or
test3.sh data assign4.template '12/16/2021' output

Loop through filenames and delete last n charaters

I have many fq.gz files in a directory and I want to loop through each filename, delete the last 8 characters and print this to a sample name text file
eg.
984674-TAATGAGC-GCTGTAGA--R1.fq.gz
984674-TAATGAGC-GCTGTAGA--R2.fq.gz
will become:
984674-TAATGAGC-GCTGTAGA--
984674-TAATGAGC-GCTGTAGA--
At the moment I have a bash script to put each filename into an array and then print the array to a sample.txt file. I have then tried to mv the filename to the new desired filename:
#!/bin/bash
declare -a FILELIST
for f in *.gz; do
#FILELIST[length_of_FILELIST + 1]=filename
FILELIST[${#FILELIST[#]}+1]=$(echo "$f");
done
printf '%s\n' "${FILELIST[#]:1:10}" > sample.txt
sample_info=sample.txt
sample_files=($(cut -f 1 "$sample_info"))
for file in "${sample_files[#]}"
do
mv "$file" "${file::(-8)}"
echo $file
done
But the script isn't removing any characters. Can you help?
to loop through each filename, delete the last 8 characters and print this to a sample name, would this work for you:
for i in *fq.gz
do
echo ${i:0:-8}
done
Using substring removal, here. Assuming you want exactly 8 characters out from the end:
for n in *.fq.gz
do
echo "${n%%??.fq.gz}"
done
For a test,
$ n="984674-TAATGAGC-GCTGTAGA--R1.fq.gz"
$ echo "${n%%??.fq.gz}"
984674-TAATGAGC-GCTGTAGA--
OR
$ echo "${n%%????????}"
984674-TAATGAGC-GCTGTAGA--

Get parent directory name and filename without extension from full filepath in bash?

I have a long file path, like
/path/to/file/dir1/dir2/file.txt.gz
I am interested in getting the file name without the last extension (i.e., file.txt), and the name of the parent directory (dir2, I don't want the full path to the parent directory, just its name).
How can I do this in bash?
Using BASH:
s='/path/to/file/dir1/dir2/file.txt.gz'
file="${s##*/}"
file="${file%.*}"
echo "$file"
file.txt
filder="${s%/*}"
folder="${folder##*/}"
echo "$folder"
dir2
Using awk:
awk -F '/' '{sub(/\.[^.]+$/, "", $NF); print $(NF-1), $NF}' <<< "$s"
dir2 file.txt
To read them into shell variables:
read folder file < <(awk -F '/' '{sub(/\.[^.]+$/, "", $NF);print $(NF-1), $NF}'<<<"$s")
The first part can be solved by basename(1):
$ basename /path/to/file/dir1/dir2/file.txt.gz
file.txt.gz
$
dirname(1) does the opposite, which is not quite what you want, but maybe you can use that as a starting point:
$ dirname /path/to/file/dir1/dir2/file.txt.gz
/path/to/file/dir1/dir2
$
Of course, you can always use Perl:
$ perl -E 'do { #p=split m|/|; say $p[-2] } for #ARGV' /path/to/file/dir1/dir2/file.txt.gz
dir2
$

How do I use Bash to create a copy of a file with an extra suffix before the extension?

This title is a little confusing, so let me break it down. Basically I have a full directory of files with various names and extensions:
MainDirectory/
image_1.png
foobar.jpeg
myFile.txt
For an iPad app, I need to create copies of these with the suffix #2X appended to the end of all of these file names, before the extension - so I would end up with this:
MainDirectory/
image_1.png
image_1#2X.png
foobar.jpeg
foobar#2X.jpeg
myFile.txt
myFile#2X.txt
Instead of changing the file names one at a time by hand, I want to create a script to take care of it for me. I currently have the following, but it does not work as expected:
#!/bin/bash
FILE_DIR=.
#if there is an argument, use that as the files directory. Otherwise, use .
if [ $# -eq 1 ]
then
$FILE_DIR=$1
fi
for f in $FILE_DIR/*
do
echo "Processing $f"
filename=$(basename "$fullfile")
extension="${filename##*.}"
filename="${filename%.*}"
newFileName=$(echo -n $filename; echo -n -#2X; echo -n $extension)
echo Creating $newFileName
cp $f newFileName
done
exit 0
I also want to keep this to pure bash, and not rely on os-specific calls. What am I doing wrong? What can I change or what code will work, in order to do what I need?
#!/bin/sh -e
cd "${1-.}"
for f in *; do
cp "$f" "${f%.*}#2x.${f##*.}"
done
It's very easy to do that with awk in one line like this:
ls -1 | awk -F "." ' { print "cp " $0 " " $1 "#2X." $2 }' | sh
with ls -1 you get just the bare list of files, then you pipe awk to use the dot (.) as separator. Then you build a shell command to create a copy of each file.
I suggest to run the command without the last sh pipe before, in order to check the cp commands are correct. Like this:
ls -1 | awk -F "." ' { print "cp " $0 " " $1 "#2X." $2 }'

Resources