File inside a directory script - linux

I have written a bash script to create a file in the directory name given. The script I wrote was:
D=$2
F=$1
D=`cat > "$F" && mkdir -v "$D"`
When I run the script passing the 2 arguments filename and pathname, it shows the file as being created but when I search for that file it not showing up in that directory.

Your order or creation is wrong, use something like this:
~/temp5$ ls
script1.bash
~/temp5$ cat script1.bash
D=$2
F=$1
mkdir -v "$D" && > "$D/$F"
~/temp5$ ./script1.bash newfilename newdirname
mkdir: created directory ‘newdirname’
~/temp5$ ls
newdirname script1.bash
~/temp5$ ls newdirname/
newfilename
$
This is the final script:
D=$2
F=$1
mkdir -v "$D" && > "$D/$F"
Edit1:
$ ls
script.bash
~/temp5$ cat script.bash
D=$2
F=$1
[[ "$D$F" =~ ^[a-zA-Z0-9]+$ ]] && mkdir -v "$D" && > "$D/$F"
~/temp5$ ./script.bash newfile123 newdir123
mkdir: created directory ‘newdir123’
~/temp5$ ls
newdir123 script.bash
~/temp5$ ls newdir123/
newfile123
~/temp5$ ./script.bash 'newfile;123' newdir123
~/temp5$ ./script.bash newfile.123 newdir123
~/temp5$ ls
newdir123 script.bash
~/temp5$ ls newdir123/
newfile123
$
This is the final script:
D=$2
F=$1
[[ "$D$F" =~ ^[a-zA-Z0-9]+$ ]] && mkdir -v "$D" && > "$D/$F"
[[ "$D$F" =~ ^[a-zA-Z0-9]+$ ]] ensures that both directory and file names have only these(1 or more alphanumeric/numeric) characters: a-z, A-Z and 0-9 using regular expressions.

You order of actions is wrong: you create the file before creating the directory. You should have done something like
mkdir "$D"
chdir "$D"
cat > "$F"

This will create the directory and a blank file of desired name in provided location
mkdir -p $2 && touch $2/$1

Related

is there a touch that can create parent directories like mkdir -p?

I have the following two functions defined in my .zshrc
newdir(){ # make a new dir and cd into it
if [ $# != 1 ]; then
printf "\nUsage: newdir <dir> \n"
else
/bin/mkdir -p $1 && cd $1
fi
}
newfile() { # make a new file, open it for editing, here specified where
if [ -z "$1" ]; then
printf "\nUsage: newfile FILENAME \n"
printf "touches a new file in the current working directory and opens with nano to edit \n\n"
printf "Alternate usage: newfile /path/to/file FILENAME \n"
printf "touches a new file in the specified directory, creating the diretory if needed, and opens to edit with nano \n"
elif [ -n "$2" ]; then
FILENAME="$2"
DIRNAME="$1"
if [ -d "$DIRNAME" ]; then
cd $DIRNAME
else
newdir $DIRNAME
fi
else
FILENAME="$1"
fi
touch ./"$FILENAME"
nano ./"$FILENAME"
}
but I am wondering, is there a version of touch that acts similar to mkdir -p, in that it can create parent dirs as needed in one line/command?
There is no touch that can create parent directory path, so write your own in standard POSIX-shell grammar that also works with zsh:
#!/usr/bin/env sh
touchp() {
for arg
do
# Get base directory
baseDir=${arg%/*}
# If whole path is not equal to the baseDire (sole element)
# AND baseDir is not a directory (or does not exist)
if ! { [ "$arg" = "$baseDir" ] || [ -d "$baseDir" ];}; then
# Creates leading directories
mkdir -p "${arg%/*}"
fi
# Touch file in-place without cd into dir
touch "$arg"
done
}
With zsh you can do:
mkdir -p -- $#:h && : >>| $#
mkdir is given the "head" of each argument to make the directories (man zshexpn says the :h expansion modifier works like the dirname tool). Then, assuming you have not unset the MUTLIOS option, the output of : (a command that produces no output) is appended to the files.

Recursion in BASH

My task is create script which lists all subdirs in user's directory and write them in a file.
I created a recursive function which should run through all directories from main and write the names of their subdirs in the file. But the script runs for first folders in my home dirs until reaching the folder without the subfolder. How do I do it correctly?
#!/bin/bash
touch "/home/neo/Desktop/exercise1/backup.txt"
writeFile="/home/neo/Desktop/exercise1/backup.txt"
baseDir="/home/neo"
print(){
echo $1
cd $1
echo "============">>$writeFile
pwd>>$writeFile
echo "============">>$writeFile
ls>>$writeFile
for f in $("ls -R")
do
if [ -d "$f" ]
then
print $1"/"$f
fi
done
}
print $baseDir
to get all folders within a path you can simply do:
find /home/neo -type d > /home/neo/Desktop/exercise1/backup.txt
done
Try this
fun(){
[[ -d $1 ]] && cd $1
echo $PWD
d=$(echo *)
[[ -d $d ]] && cd $d || return
fun
}

Recycle Bin in Bash Script

I am trying to create a basic recycle bin concept in a VM using bash scripting. It will need to delete files that have been entered and place them into a directory that is created and save the path(origin) to a log file to be later used in a restore function.
I will start off with my delete/recycle code which I believe works just fine but seems kind of untidy/contains redundant code:
#!/bin/sh
if [ ! -d ~/recycle ]
then mkdir ~/recycle
fi
if [ ! -d ~/recycle/recycle_log ]
then mkdir ~/recycle/recycle_log
fi
if [ ! -d ~/recycle/recycle_bin ]
then mkdir ~/recycle/recycle_bin
fi
if [ -d ~/recycle ]
then
echo "$(readlink -f "$1")" >> "$HOME/recycle/recycle_log/log_file" && mv "$1" "$HOME/recycle/recycle_bin"
echo "$(readlink -f "$2")" >> "$HOME/recycle/recycle_log/log_file" && mv "$2" "$HOME/recycle/recycle_bin"
echo "$(readlink -f "$3")" >> "$HOME/recycle/recycle_log/log_file" && mv "$3" "$HOME/recycle/recycle_bin"
echo "$(readlink -f "$4")" >> "$HOME/recycle/recycle_log/log_file" && mv "$4" "$HOME/recycle/recycle_bin"
fi
#end
Thereafter what I have for my restore script is as follows:
#!/bin/sh
cd "$HOME/recycle/recycle_bin" || exit 1
mv -i "$(grep "$1" "$HOME/recycle/recycle_log")"
I imagine this is somewhat close to what I need to return any deleted file stored in the log/recycle bin to be restored to its origin but the error I am getting is:
mv: missing destination file operand after `'
Any thoughts on where I'm going wrong?
Try this:
recycle.sh
#!/bin/sh
set -e
check_dir() {
[ ! -d $1 ] || return 0
mkdir --parents $1
}
check_dir "${HOME}/recycle/recycle_bin"
touch "${HOME}/recycle/recycle_log"
for file in "$#"; do
echo "$(readlink -f "$file")" >> "${HOME}/recycle/recycle_log"
mv "$file" "${HOME}/recycle/recycle_bin"
done
#end
restore.sh
#!/bin/sh
set -e
cd "${HOME}/recycle/recycle_bin" || exit 1
for name in "$#"; do
file=$(grep "\/${name}\$" "${HOME}/recycle/recycle_log")
mv -i $name "$file"
sed -i "/\/${name}\$/ d" "${HOME}/recycle/recycle_log"
done
Some insights:
set -e: Abort on any error, to avoid some if's
$#: The array of arguments ($1, $2...)
[ ! -d $1 ] || return 0: Since we are using set -e, do not fail if the directory exists
grep "\/${name}\$" ...: Only matches the name at the end of the path
sed -i: sed in-place editing to remove the line

Bash script to copy the directory structure from source directory into target directory

I am very new to Bash scritping and to get some practice, I am attempting to write a script that takes in a source directory and a destination directory. The script will search the source directory and copy its structure of sub-directories into the target directory (any files will be ignored, just the directories themselves will be duplicated). The source directory can have any number of sub-directories at any depth. What would be the best way to achieve this? I have started by attempting to write a recursive function where, if a directory is found, the function is called recursively onto this directory. However, due to my lack of experience with scripting, I have been stumped.
Here is what I have so far:
#! /bin/bash
if [ ! $# -eq 2 ]; then
echo "ERROR: Script needs 2 arguments: $0 source/directory/ target/directory/"
exit
fi
SOURCE_DIR=$1
TARGET_DIR=$2
function searchForDirectory {
for FILE in ls $SOURCE_DIR/*; do #should be 'ls *current_directory*'
if [ -d $FILE ]; then
searchForDirectory #call function recursively onto $FILE
#Do stuff here to create copy of this directory in target dir
fi
done
}
if [ ! -d $SOURCE_DIR ]; then
echo "ERROR: Source directory does not exist"
exit
else
searchForDirectory
fi
I know that this is just a skeleton function, and a lot more work would need to be put into it, but I'm just looking for guidance as to whether this is the correct way to go about this before I go any further, and if so, what would be my next step? How do I pass a directory into my function?
EDIT: Here is my solution thanks to Ivan's help below
#! /bin/bash
if [ ! $# -eq 2 ]; then
echo -e "\nERROR: Script needs 2 arguments:\n$0 source/directory/ target/directory/\n"
exit
fi
function recursiveDuplication {
for file in `ls $1`; do
if [ -d $1/$file ] && [ ! -d $2/$file ]; then
mkdir $2/$file
recursiveDuplication $1/$file $2/$file
elif [[ $1/$file == *.png ]]; then
convert $1/$file $2/$file
fi
done
}
if [ ! -d $1 ]; then
echo -e "\nERROR: $1 does not exist\n"
exit
elif [ -d $2 ] && [ ! -w $2 ]; then
echo -e "\nERROR: You do not have permission to write to $2\n"
exit
elif [ ! -d $2 ]; then
echo -e "\nSUCCESS: $2 has been created"
mkdir $2
fi
recursiveDuplication $1 $2
There are two issues with this solution:
As Rany Albeg Wein explained below, using 'ls' is not a good solution - and I have seen why when there is a space in a directory/*.png name.
I am also attempting to copy any *.png file from the source to the target and convert it to a *.jpg image in the target. How can I do this? I am attempting to use ImageMagick's convert image.png image.jpg command, but do not know how to do so when the image.png is being referred to as $file?
you can simplify a lot
$ find a -type d | xargs -I d mkdir -p copy/d
copy the tree structure from directory a to new directory under copy
$ tree a
a
|-- b
| |-- c
| | `-- file4
| |-- d
| | `-- file4
| `-- file3
`-- file2
3 directories, 4 files
$ tree copy
copy
`-- a
`-- b
|-- c
`-- d
4 directories, 0 files
This solution has been tested with directory names containing special chars, a loop in the directory tree and a broken symbolic link.
There is bellow a stansard solution:
( cd source/ ; find . -depth -type d -printf "%p\0" | xargs -0 -I xxx mkdir -p ../destination/xxx )
And there is a second solution using gnu cpio:
( cd source/ ; find . -depth -type d -printf "%p\0" | cpio --null -pd ../destination )
The test:
$ mkdir -p source/a/ba/c/d/e
$ mkdir -p source/a/bb/c/d/e
$ mkdir -p source/a/b/ca/d/e
$ ln -s source/broken/link source/a/ba/c/d/e/broken
$ (cd source/a/ba && ln -s ../../a tree-loop)
$ mkdir -p source/z/"$(printf "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\40\41\42\43\44\45\46\47\72\73\74\75\76\77\100\133\134\135\1336\137\140\173\174\175\176\177dir")"
$ touch source/a/b/"$(printf "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\40\41\42\43\44\45\46\47\72\73\74\75\76\77\100\133\134\135\1336\137\140\173\174\175\176\177file")"
$ ls
source
$ find source -depth
source/z/??????????????????????????????? !"#$%&':;<=>?#[\][6_`{|}~?dir
source/z
source/a/ba/tree-loop
source/a/ba/c/d/e/broken
source/a/ba/c/d/e
source/a/ba/c/d
source/a/ba/c
source/a/ba
source/a/b/ca/d/e
source/a/b/ca/d
source/a/b/ca
source/a/b/??????????????????????????????? !"#$%&':;<=>?#[\][6_`{|}~?file
source/a/b
source/a/bb/c/d/e
source/a/bb/c/d
source/a/bb/c
source/a/bb
source/a
source
$ ( cd source/ ; find . -depth -type d -printf "%p\0" | xargs -0 -I xxx mkdir -p ../destination/xxx )
$ find destination/ -depth
destination/z/??????????????????????????????? !"#$%&':;<=>?#[\][6_`{|}~?dir
destination/z
destination/a/ba/c/d/e
destination/a/ba/c/d
destination/a/ba/c
destination/a/ba
destination/a/b/ca/d/e
destination/a/b/ca/d
destination/a/b/ca
destination/a/b
destination/a/bb/c/d/e
destination/a/bb/c/d
destination/a/bb/c
destination/a/bb
destination/a
destination/
The gnu cpio test:
$ rm -rf destination
$ ( cd source/ ; find . -depth -type d -printf "%p\0" | cpio --null -pd ../destination )
0 blocks
$ find destination -depth
destination/z/??????????????????????????????? !"#$%&':;<=>?#[\][6_`{|}~?dir
destination/z
destination/a/ba/c/d/e
destination/a/ba/c/d
destination/a/ba/c
destination/a/ba
destination/a/b/ca/d/e
destination/a/b/ca/d
destination/a/b/ca
destination/a/b
destination/a/bb/c/d/e
destination/a/bb/c/d
destination/a/bb/c
destination/a/bb
destination/a
destination
#!/bin/bash
# 1st argument - source dir, 2nd - destination
function rrr {
for i in `ls $1`
do
if [ -d $1/$i ]
then
mkdir $2/$i
rrr $1/$i $2/$i
fi
done
}
rrr $1 $2

Redirecting vcftools file in linux - tips

Here is the code that gets the VCF file from a specific region using tabix and then filters it for specific (european) population using 'keep' option from vcftools.
####select specific population
if [ "$POP_FILE" != "" ]; then
vcftools --vcf temp.vcf --keep $POP_FILE --recode --recode-INFO-all > temp2.vcf 2> /dev/null
else
cp -f temp.vcf temp2.vcf
fi
PROBLEM: it creates the recode.vcf file but then the redirection is not happening as the temp2 file is empty
I would avoid vcftools and use bcftools (https://github.com/samtools/bcftools) instead:
if [ "$POP_FILE" != "" ]; then
bcftools view temp.vcf -S $POP_FILE -o temp2.vcf
else
cp -f temp.vcf temp2.vcf
fi
To install bcftools:
git clone --branch=develop git://github.com/samtools/bcftools.git
git clone --branch=develop git://github.com/samtools/htslib.git
cd htslib && make && cd ..
cd bcftools && make && cd ..
sudo cp bcftools/bcftools /usr/local/bin/

Resources