Change directories in shell scripts with string variables - linux

I have a series of directories that are only different by a numerical tag.
arr=(0 1 2 3)
i=0
while [ $i -le ${arr}]
do
dir="~Documents/seed"
dir+=${arr[i]}
echo $dir #works
cd dir #directory not found
#do other things#
done
Is it possible to do this?

This might be easier:
#!/bin/bash
for d in ~/Dcouments/seed*
do
if [ -d "$d" ]; then
echo $d
fi
done
Note:
You have tarfiles in ~/Documents too (with names that also match the wildcard), so I have added an if statement that checks if it is a directory or a file and only reacts to directories.

Related

How can I batch rename multiple images with their path names and reordered sequences in bash?

My pictures are kept in the folder with the picture-date for folder name, for example the original path and file names:
.../Pics/2016_11_13/wedding/DSC0215.jpg
.../Pics/2016_11_13/afterparty/DSC0234.jpg
.../Pics/2016_11_13/afterparty/DSC0322.jpg
How do I rename the pictures into the format below, with continuous sequences and 4-digit padding?
.../Pics/2016_11_13_wedding.0001.jpg
.../Pics/2016_11_13_afterparty.0002.jpg
.../Pics/2016_11_13_afterparty.0003.jpg
I'm using Bash 4.1, so only mv command is available. Here is what I have now but it's not working
#!/bin/bash
p=0
for i in *.jpg;
do
mv "$i" "$dirname.%03d$p.JPG"
((p++))
done
exit 0
Let say you have something like .../Pics/2016_11_13/wedding/XXXXXX.jpg; then go in directory .../Pics/2016_11_13; from there, you should have a bunch of subdirectories like wedding, afterparty, and so on. Launch this script (disclaimer: I didn't test it):
#!/bin/sh
for subdir in *; do # scan directory
[ ! -d "$subdir" ] && continue; # skip non-directory
prognum=0; # progressive number
for file in $(ls "$dir"); do # scan subdirectory
(( prognum=$prognum+1 )) # increment progressive
newname=$(printf %4.4d $prognum) # format it
newname="$subdir.$newname.jpg" # compose the new name
if [ -f "$newname" ]; then # check to not overwrite anything
echo "error: $newname already exist."
exit
fi
# do the job, move or copy
cp "$subdir/$file" "$newname"
done
done
Please note that I skipped the "date" (2016_11_13) part - I am not sure about it. If you have a single date, then it is easy to add these digits in # compose the new name. If you have several dates, then you can add a nested for for scanning the "date" directories. One more reason I skipped this, is to let you develop something by yourself, something you can be proud of...
Using only mv and bash builtins:
#! /bin/bash
shopt -s globstar
cd Pics
p=1
# recursive glob for .jpg files
for i in **/*.jpg
do
# (date)/(event)/(filename).jpg
if [[ $i =~ (.*)/(.*)/(.*).jpg ]]
then
newname=$(printf "%s_%s.%04d.jpg" "${BASH_REMATCH[#]:1:2}" "$p")
echo mv "$i" "$newname"
((p++))
fi
done
globstar is a bash 4.0 feature, and regex matching is available even in OSX's anitque bash.

Merge files to directories based on match of filename to directory name

I am pretty new to scripting so please be easy. I am aware that there is another forum that is related to this but does not exactly cover my issue.
I have a directory containing files and another directory containing the corresponding folders that I need to move each file to. Each file corresponds to the destination directory like:
DS-123.txt
/DS-123_alotofstuffhere/
I would like to automate the move based on a match of the first 6 characters of the filename to the first 6 of the directory.
I have this:
filesdir=$(ls ~/myfilesarehere/)
dir=$(ls ~/thedirectoriesareinthisfolder/)
for i in $filesdir; do
for j in $dir; do
if [[${i:6} == ${j:6}]]; then
cp $i $j
fi
done
done
But when I run the script, I get the following error:
es: line 6: [[_DS-123_morefilenametext.fasta: command not found
I am using Linux (not sure what version on the supercomputer, sorry).
It's better to use arrays and globbing to hold the list of files and directories, instead of ls. With that change and a correction to the [[ ... ]] part, you code us this:
files=(~/myfilesarehere/*)
dirs=(~/thedirectoriesareinthisfolder/*)
for i in "${files[#]}"; do
[[ -f "$i" ]] || continue # skip if not a regular file
for j in "${dirs[#]}"; do
[[ -d "$j" ]] || continue # skip if not a directory
ii="${i##*/}" # get the basename of file
jj="${j##*/}" # get the basename of dir
if [[ ${ii:0:6} == ${jj:0:6} ]]; then
cp "$i" "$j"
# need to break unless a file has more than one destination directory
fi
done
done
[[ -d "$j" ]] check is necessary because your dirs array could contain some files too. To be safer, I have added a check for $i being a file as well.
Here is the solution that doesn't use arrays, as suggested by #triplee:
for i in ~/myfilesarehere/*; do
[[ -f "$i" ]] || continue # skip if not a regular file
for j in ~/thedirectoriesareinthisfolder/*; do
[[ -d "$j" ]] || continue # skip if not a directory
ii="${i##*/}" # get the basename of file
jj="${j##*/}" # get the basename of dir
if [[ ${ii:0:6} == ${jj:0:6} ]]; then
cp "$i" "$j"
# need to break unless a file has more than one destination directory
fi
done
done

Create .txt of all files in each subdirectory

I need to create a text file in each subdirectory of all files in the list.
For example, subdirectory1 would contain a list of all of its files as a .txt and subdirectory2 would also contain a list of all of subdirectory2 files as a .txt.
I have tried
#!/bin/bash
for X in "$directory" *
do
if [ -d "$X" ];
then
cd "$X"
files="$(ls)"
echo "$files" >> filesNames.txt
fi
done
However this did not generate anything. I absolutely need it as a shell script because it will be part of a pipeline script, but I cannot seem to get it to work.
Here is the adjusted script giving me the no such file or directory comment. I know that the folder exists and have used it in commands that are run before this command.
#!/bin/bash
#Retrieve the base directory path
baseDir=$(dirname "$ini")
#Retrieve the reference genome path
ref=$(dirname "$genome")
#Create required directory structure
tested="$baseDir/tested"
MarkDups1="$baseDir/MarkDups1"
#don't create if already exists
[[ -d "tested" ]] || mkdir "$tested"
[[ -d "MarkDups1" ]] || mkdir "$MarkDups1"
#create a text file with all sorted and indexed bam files paths
#!/bin/bash
for x in $MarkDups1/*/;
do
(cd "$x"; ls > filesNames.txt)
done
The sequence to iterate over should be "$directory"/*/.
for x in "$directory"/*/; do
(cd "$x"
files=(*)
printf '%s\n' "${files[#]}" > filesNames.txt
)
done

What is the error in this shell script

I never used shell script, but now I have to , here is what I'm trying to do :
#!/bin/bash
echo running the program
./first
var = ($(ls FODLDER |wc -l)) #check how many files the folder contains
echo $var
if( ["$var" -gt "2"] #check if there are more the 2file
then ./second
fi
the scriopt crashes at the if statement. how may I solve this
Many:
var = ($(ls FODLDER |wc -l))
This is wrong, you cannot have those spaces around =.
if( ["$var" -gt "2"]
Your ( is not doing anything there, so it has to be deleted. Also, you need spaces around [ and ].
All together, this would make more sense:
#!/bin/bash
echo "running the program"
./first
var=$(find FOLDER -maxdepth 1 -type f|wc -l) # better find than ls
echo "$var"
if [ "$var" -gt "2" ]; then
./second
fi
Note:
quote whenever you echo, specially when handling variables.
see another way to look for files in a given path. Parsing ls is kind of evil.
indent your code for better readibility.
Edit your script.bash file as follow:
#!/bin/env bash
dir="$1"
echo "running the program"
./first
dir_list=( $dir/* ) # list files in directory
echo ${#dir_list[#]} # count files in array
if (( ${#dir_list[#]} > 2 )); then # test how many files
./second
fi
Usage
script.bash /tmp/
Explaination
You need to learn bash to avoid dangerous actions!
pass the directory to work with as first argument in the command line (/tmp/ → `$1)
use glob to create an array (dir_list) containing all file in given directory
count items in array (${#dir_list[#]})
test the number of item using arithmetic context.

Batch Renaming multiple files with different extensions Linux Script?

I would like to write a linux script that will move or copy all files with the same filename (but different extensions) to a new filename for all those files, while maintaining their different extensions. In other words:
if I start with a directory listing:
file1.txt, file1.jpg, file1.doc, file12.txt, file12.jpg, file12.doc
I would like to write a script to change all the filenames without changing the extensions. For the same example, choosing file2 as the new filename the result would be:
file2.txt, file2.jpg and file2.doc, file12.txt, file12.jpg, file12.doc
So the files whose filename do not match the current criteria will not be changed.
Best wishes,
George
Note: If there's file1.doc in variable i, expression ${i##*.} extracts extension i.e. doc in this case.
One line solution:
for i in file1.*; do mv "$i" "file2.${i##*.}"; done
Script:
#!/bin/sh
# first argument - basename of files to be moved
# second arguments - basename of destination files
if [ $# -ne 2 ]; then
echo "Two arguments required."
exit;
fi
for i in $1.*; do
if [ -e "$i" ]; then
mv "$i" "$2.${i##*.}"
echo "$i to $2.${i##*.}";
fi
done
The util-linux-ng package (most of linux flavours have it installed by default) has the command 'rename'. See man rename for use instructions. Using it your task can be done simply as that rename file1 file2 file1.*
To handle input files whose basenames contain special characters, I would modify plesiv's script to the following:
if [ $# -ne 2 ]; then
echo "Two arguments required."
exit;
fi
for i in "$1".*; do
if [ -e "$i" ]; then
mv "$i" "$2.${i##*.}"
echo "$i to $2.${i##*.}";
fi
done
Note the extra quotes around $1.

Resources