I have three folders, /ftp/A, /ftp/B, /ftp/C but logically C is a subdirectory of B and B is a subdirectory of A.
I try to resolv this using a script at system startup that binds the folders.
I have a text file in /ftp/dirFolder where there are the relationships between the folders. Like
B C
A B C
A B
The first column is the father, the second one is the son and the third one is the nephew.
The script is this:
case "$1" in
start) while IFS=' ' read -r x y z
do
if [ -z "$z" ]; then
mkdir -p /ftp/$x/$y
mount -t none --bind /ftp/$y /ftp/$x/$y > /dev/null 2>&1
else
mkdir -p /ftp/$x/$y/$z
mount -t none --bind /ftp/$z /ftp/$x/$y/$z > /dev/null 2>&1
fi
done < /ftp/dirFolder
;;
stop) while IFS=' ' read -r x y z
do
if [ -z "$z" ]; then
umount /ftp/$x/$y > /dev/null 2>&1
else
umount /ftp/$x/$y/$z > /dev/null 2>&1
fi
done < /ftp/dirFolder
;;
The problem is: when I start the script, the folders are created and mounted correctly, but if inside C there is a folder or a file, it won't be visible inside /ftp/A/B/C but they will inside /ftp/B/C.
mount command gives me this:
/ftp/C on /ftp/B/C type none (rw,bind)
/ftp/C on /ftp/A/B/C type none (rw,bind)
Is there a way to make the folder C available from both folder A and B?
Solved, it was an error on the dirFolder file.
First you need to mount the nephew folder in the son's folder, then mount the son's folder in the father's one.
I was:
B C
A B C
A B
It should be:
B C
A B
A B C
Hope it helps someone!
Related
I want to make a command/function to mkdir and touch my directory and files quickly.
Terminal: cd PROJECT:
PROJECT
Terminal: quickcreate home index.js style.css .... The tree looks like:
PROJECT __ home __ index.html
\_ style.css
\_ ...
Do manually:
mkdir home
touch home/index.html
touch home/style.css
touch home/...
I want to write a command like this:
function quickcreate {
if [ $# -eq 0 ]
then
echo "No arg supplied!"
return 0
else
mkdir $1
# how can I do with S2, S3, .... to touch S1/S2 S1/S3...?
}
I recommend -p.
qc() { local p="$1";
if [[ -n "$p" ]];
then mkdir -p "$p" # can be any full or relative path;
else echo "Use: qc <dirpath> [f1[..fN]]"; return 1;
fi;
shift;
for f; do touch "$p/$f"; done;
}
$: qc
Use: qc <dirpath> [f1[..fN]]
$: cd /tmp
$: qc a/b/c 5 4 3 2 1 # relative path
$: qc a/b # no files; dir already exists; no problem
$: qc /tmp/a/b/c/d 3 2 1 # full path that partially exists
$: find a # all ok
a
a/b
a/b/c
a/b/c/1
a/b/c/2
a/b/c/3
a/b/c/4
a/b/c/5
a/b/c/d
a/b/c/d/1
a/b/c/d/2
a/b/c/d/3
You can use shift to remove positional arguments one by one.
Don't forget to double quote the directory and file names so the script works for names containing whitespace, too.
mkdir "$1"
dir=$1
shift
while (( $# )) ; do
touch "$dir/$1"
shift
done
This is another method you can use for what you want, that is using arrays:
function quickcreate {
if [ $# -eq 0 ]; then
echo "No arg supplied!"
return 0
else
dir="${#::1}"
files=(${#:1})
mkdir "$dir"
for file in "${files[#]}"; do
touch $dir/$file
done
}
I have yet another question. This code is for the rcS script in a Linux distro I'm making and for some reason occasionally it segfaults. About 85% of the time it works just fine and all is dandy. But then there is that 15% of the time when it segfaults on either creating a directory or doing the mount.
# Find and mount all detected drives sda-sdm and partitions 0-10, you can change these by changing the values but odds are you won't have more than 26 USB drives plugged in or 10 partitions ;)
echo " ${YELLOW}[ ${BLUE}*${YELLOW} ] Finding and mounting the program drive"
for DRIVE in a b c d e f g h i j k l m n o p q r s t u v w x y z; do
mkdir "/mnt/sd${DRIVE}/"
mount "/dev/sd${DRIVE}" "/mnt/sd${DRIVE}/" 2> /dev/null
if [ ! "${?}" = '0' ]; then
rmdir "/mnt/sd${DRIVE}/"
fi
for PARTITION in 1 2 3 4 5 6 7 8 9 10; do
mkdir "/mnt/sd${DRIVE}${PARTITION}"
mount "/dev/sd${DRIVE}${PARTITION}" "/mnt/sd${DRIVE}${PARTITION}/" 2> /dev/null
if [ ! "${?}" = '0' ]; then
rmdir "/mnt/sd${DRIVE}${PARTITION}/"
fi
if [ -d /mnt/sd${DRIVE}${PARTITION}/*/program-drive-${ARCH} ]; then
if [ ! "${PROGRAM_DRIVE_FOUND}" = 'YES' ]; then
PROGRAM_DRIVE_FOUND='YES'
echo "${MESG} Found program drive on device sd${DRIVE}${PARTITION} mounted at /mnt/sd${DRIVE}${PARTITION}"
source /mnt/sd${DRIVE}${PARTITION}/*/program-drive-${ARCH}/os_info
rm "/etc/init.d/os_info"
cp /mnt/sd${DRIVE}${PARTITION}/*/program-drive-${ARCH}/os_info "/etc/init.d/os_info"
echo "${MESG} Adding user ${USER}"
/bin/adduser -s /bin/bash -G root -D "$USER"
echo "${USER}:password" | /usr/sbin/chpasswd -m > /dev/null
echo -e "${USER}\tALL=NOPASSWD: ALL" >> /etc/sudoers
mount -o loop /mnt/sd${DRIVE}${PARTITION}/*/program-drive-${ARCH}/home "/home/${USER}/" 2> /dev/null
rmdir "/home/${USER}/lost+found/" 2> /dev/null
chmod 770 "/home/${USER}/"
if [ ! "${?}" = '0' ]; then
echo "${MESG} Error mounting home on program drive! 'home' file gone or corrupt."
echo "${RED}SYSTEM BROKEN, PRESS ANY KEY TO SHUTDOWN:"
read -sn 1
poweroff
fi
fi
fi
done
done
I have a test harness called "test.sh" that will create 24 backup files in a directory called "D1" with extensions .txt~, .bak, and 12 files that start with #. In this test harness I have to call another method called "clean.sh" that will delete all the files in the given directory (in this case D1).
This is my test.sh:
#!/bin/bash
#Create new directory called D1
mkdir D1
#cd into D1
cd ./D1
#Create 12 backup files .bak in D1 (using for loop)
for i in `seq 1 12`;
do
#echo $I
touch file$i.bak D1
done
#Create 12 backup files .txt~ in D1 (using set)
touch {a..l}.txt~ D1
#Create 12 files prefix # in D1 (using while loop)
COUNTER=0
while [ $COUNTER -lt 12 ]; do
touch \#$COUNTER.txt D1
let COUNTER=COUNTER+1
done
#Once finished creating backup files in all 3 categories, do an ls -l
ls -l
#Invoke clean method here
./cleanUp.sh
#Do final ls - l
ls - l
This is clean.sh:
#!/bin/bash
#Need to pass a directory in as argument
for i in "$#"
do
echo file is: $I
done
I'm not sure how to pass in a directory as an argument and calling a method in another method is confusing. Thanks!
Your script is a nice start.
The touch commands can do without the parameter D1, your cleanup.sh call needs a parameter. The parameter ${PWD} is given by the shell, thats easy in this case.
(you can also give a parameter like /home/mina/test2/D1).
The script will look like
!/bin/bash
#Create new directory called D1
mkdir D1
#cd into D1
cd ./D1
#Create 12 backup files .bak in D1 (using for loop)
for i in `seq 1 12`;
do
#echo $I
touch file$i.bak
done
#Create 12 backup files .txt~ in D1 (using set)
touch {a..l}.txt~
#Create 12 files prefix # in D1 (using while loop)
COUNTER=0
while [ $COUNTER -lt 12 ]; do
touch \#$COUNTER.txt
let COUNTER=COUNTER+1
done
#Once finished creating backup files in all 3 categories, do an ls -l
ls -l
#Invoke clean method here
./cleanUp.sh ${PWD}
#Do final ls - l
ls - l
Now the cleanup script.
First change $I into $i, the variable name is case sensitive.
With for i in "$#" you loop through the parameters (only the name of the dir),
and you would like to go through the diffferent filenames.
Try the following alternatives:
# Let Unix expand the * variable
for i in $1/*; do
# Use a subshell
for i in $(ls "$#"); do
# use a while-loop
ls $# | while read i; do
# use find and while
find "$1" -type f | while read i; do
# use find and -exec
find "$1" -type f -exec echo File is {} \;
# use find and xargs
find "$1" -type f | xargs echo File is
(I'll reuse this as it's about the same thing).
I have managed to make a usable script that does everything what's explained below the code block, except the order of the arguments, i.e.:
./test.sh B N A
will delete B.zip, create a new archive, but stops there, A will not be processed. It's fine, I can keep N as the last argument, no problem. Also, the echo "no backup needed, removing" does not work for some reason, but that's rather optional.
My question is: can what I have done be improved/altered somehow so that if there are other folders to be added in time the only changes to the script to be the DIRx entries? The biggest problem I see is modifying the block between <start> and <stop>. Here is the script:
#!/bin/bash
DIR1=A
DIR2=B
DIR3=C
bak()
{
if [[ "$*" == N ]]
then
if [ $1 == N ]
then
:
else
echo "no backup needed, removing"
rm -v $1.zip
fi
else
if
[ -f $1.zip ]
then
/bin/mv -vi $1.zip BKP/$1.zip_`/bin/date +"%H-%M"`
else
echo "no "$1".zip"
fi
fi
}
archive()
{
if [ $* == N ]
then
:
else
if [ $1 == C ]
then
7z a -mx=9 $1.zip ../path/$1 -r -x\!$1/nope
else
7z a -mx=9 $1.zip $1 -r -x\!$1/bogus
fi
fi
}
########### <start> ####################
if [ -z "$*" ] || [[ "$#" -eq 1 && "$1" == N ]]
then
bak "$DIR1"
bak "$DIR2"
bak "$DIR3"
archive "$DIR1"
archive "$DIR2"
archive "$DIR3"
fi
############## <stop> #####################
#if [[ "$#" -eq 1 && "$1" == N ]]
#then
# rm -v "$DIR1".zip
# rm -v "$DIR2".zip
# rm -v "$DIR3".zip
# archive "$DIR1"
# archive "$DIR2"
# archive "$DIR3"
#fi
if [[ "$#" -gt 1 && "$*" == *N* ]]
then
while [ "$#" -gt 1 ]
do
if [ "$1" == N ]
then
:
else
rm -v "$1".zip
fi
archive "$1"
shift
done
else
while [ "$#" -ge 1 ]
do
bak "$1"
archive "$1"
shift
done
fi
exit 0
Now, here's what I have and want it to do. The current directory holds the script, test.sh, and the folders A and B. ls -AR produces this:
A B test.sh
./A:
1.txt 2.txt bogus
./B:
3.txt 4.txt bogus
There is another folder, C, in ../path/. The same ls -AR ../path gives this:
../path:
C
../path/C:
5.txt 6.txt nope
../path/C/nope:
q.doc w.rtf
What I want the script to do. When run with no arguments:
./test.sh
1) checks for existing zip archives in the current directory
1.a) if they exist, a backup is made for each with additional date suffix into BKP/.
2.a) if not, it lets you know
2) the three folders, A, B and C are archived, folders A and B without A/bogus and B/bogus and folder C without ../path/C/nope/* and ../path/C/nope/ .
If run with arguments, these can be any of A, B or C, with an optional N. If run with N, only:
./test.sh N
then no archive check/backup will be performed, any archives already existent will be deleted and all 3 folders get archived. If run with any combination of A, B or C, for example:
./test.sh A C
then only archives A.zip and C.zip have a check and backup and only folders A and C are archived, A without A/bogus and C without ../path/C/nope/* and ../path/C/nope/ . If run with any combination of A, B or C, but with additional N, i.e.:
./test.sh B N C
Then no check/backup is performed for B.zip and C.zip, the archives (if existent) get deleted and the folders B and C are archived.
The archives will have (inside) the folder as the root directory (i.e. open up the archive and you'll see A, B or C first) and all three of them have exceptions to the list of files to be processed: A and B don't need bogus, while C doesn't need subfolder none and anything inside it. I use 7z instead of zip because I can write:
7z a x.zip ../path/./C/bla/bla
and have C as the root directory; I couldn't do it with zip (most likely I don't know how to, it doesn't matter as long as it works).
So far, the checking and the backup work. The archiving, if no exceptions are added and I remove the $PATH thing, work. The whole script doesn't. I would have posted every combination I have done so far, but 99% of them would have probably been impossible and the rest childish. I couldn't care less how it looks as long as it does the job.
Very optional: can an alias (or some sort) like "SCF" be made to "Supercalifragilistic"? The C folder has a rather long name (I could just make a symlink, I know). I have no idea about this one.
I managed to do it. It's probably very childish and the worst possible script but I don't care right now, it works. Of course, if somebody has a better alternative to share, it's welcome, until then here's the whole script:
#!/bin/bash
# ./test.sh = 1. searches for existing archives
# 1.a. if they exist, it backups them into BKP/.
# 1.b. if not, displays a message
# 2. archives all the directories in the array list
# ./test.sh N = 1. deletes all the folder's archives existent and
# specified in the array list
# 2. archives all the directories in the array list
# ./test.sh {A..F} = 1. searches for existing archives from arguments
# 1.a. if they exist, it backups them into BKP/.
# 1.b. if not, displays a message
# 2. archives all the directories passed as arguments
# ./test.sh {A..F} N = 1. deletes all the archives matching $argument.zip
# 2. archives all the directories passed as arguments
# The directories to be backed-up/archived, all in the current (script's) path
# except "C", on a different path
DIR=(A B C D E F)
# The back-up function, if any argument is "N", processing it is omitted
bak()
{
if [[ "$*" == N ]]
then
:
else
if
[ -f $1.zip ]
then
mv -vi $1.zip BKP/$1.zip_`date +"%H-%M"`
else
echo "$(tput setaf 1) no "$1".zip$(tput sgr0)"
fi
fi
}
# The archive function, if any argument is "N", processing it is omitted. Folder
# "C" has special treatment
archive()
{
if [ $* == N ]
then
:
else
if [ $1 == C ]
then
7z a -mx=9 $1.zip ../path/$1 -r -x\!$1/nope
else
7z a -mx=9 $1.zip $1 -r -x\!$1/bogus
fi
fi
}
# case #1: no arguments
if [ -z "$*" ]
then
for i in $(seq 0 $((${#DIR[#]}-1))) # counts from 0 to array-1
do
echo "$(tput setaf 2) backup$(tput sgr0)"
bak "${DIR[i]}"
archive "${DIR[i]}"
done
exit $?
fi
# case #2: one argument, "N"
if [[ "$#" -eq 1 && "$1" == N ]]
then
for i in $(seq 0 $((${#DIR[#]}-1)))
do
echo "$(tput setaf 1) no backup needed, removing$(tput sgr0)"
rm -v "${DIR[i]}".zip
archive "${DIR[i]}"
done
exit $?
fi
# case #3: folders as arguments with "N"
if [[ "$#" -gt 1 && "$*" == *N* ]]
then
while [ "$#" -gt 1 ]
do
if [ "$1" == N ]
then
:
else
echo "$(tput setaf 1) no backup needed, removing$(tput sgr0)"
rm -v "$1".zip
fi
archive "$1"
shift
done
# case #4: folders as arguments without "N"
else
while [ "$#" -ge 1 ]
do
echo "$(tput setaf 2) backup$(tput sgr0)"
bak "$1"
archive "$1"
shift
done
fi
exit $?
Now I have the rename script like this:
cd /Users/KanZ/Desktop/Project/Test/
n=1
for file in *.jpg; do
echo $file
prefix=M
file_name=M$n.jpg
echo $file_name
n=$(( $n+1 ))
mv $file $file_name
done
The first if I run script, the all of jpg files will be M1.jpg M2.jpg M3.jpg but if I add the new file name A1.jpg to this directory and run script again. All of M1.jpg M2.jpg M3.jpg will be replaced by M4.jpg(before run script, this file named A1.jpg) because the first letter is A. It come before M. This is my problem. I would like to get M1 M2 M3 M4.jpg and every time if there are new files coming, this script will generate the continue name like M5.jpg M6.jpg . How is the code look like? Hope you understand that. Thank u for helping
The other answers given so far don't show you any good practice.
Here's one possibility that assumes that the numbering is continuous (i.e., no jump from M4.jpg to M6.jpg). Without that assumption, the script is safe (it won't overwrite existing files, but will not rename certain files).
#!/bin/bash
shopt -s extglob
shopt -s nullglob
cd /Users/KanZ/Desktop/Project/Test/
# Determine how many files MX.jpg you have (with X a number):
files=( M+([[:digit:]]).jpg )
n=${#files[#]}
for file in !(M+([[:digit:]])).jpg; do
file_name=M$((++n)).jpg
mv -vn -- "$file" "$file_name"
done
The -n option to mv: no clobber (do not overwrite an already existing file)
The -v option to mv: be verbose (so you don't need the echos you had)
The -- thing: end of options, only arguments will be found behind (just in case some files will start with a hyphen, that would confuse mv).
This little script will do it.
cd /Users/KanZ/Desktop/Project/Test/
n=$(ls -1 | grep '^M' -c) # find the number for M files
ls -1 *.jpg| grep -v '^M' | while read file # for each of the non-M files
do
n=$(($n+1));
new_file=M$n.jpg # new name with next available M file name
mv "$file" "$new_file" # rename with new file name
done
See how it works.
test/rename$ for x in 1 2 3; do echo A$x> A$x.jpg; done;
test/rename$ ls
A1.jpg A2.jpg A3.jpg
test/rename$ cat ../rnm.sh
#!/bin/bash
cd "$1"
n=$(ls -1 | grep '^M' -c)
nfiles=$(ls -1 *.jpg| grep -v '^M');
for file in $nfiles;
do
n=$(($n+1));
new_file=M$n.jpg
mv $file $new_file
done
test/rename$ bash ../rnm.sh ./
test/rename$ ls
M1.jpg M2.jpg M3.jpg
test/rename$ for x in 1 2 ; do echo B$x> B$x.jpg; done;
test/rename$ ls
B1.jpg B2.jpg M1.jpg M2.jpg M3.jpg
test/rename$ bash ../rnm.sh ./
test/rename$ ls
M1.jpg M2.jpg M3.jpg M4.jpg M5.jpg
test/rename$ for x in *.jpg; do echo $x; cat $x; done;
M1.jpg
A1
M2.jpg
A2
M3.jpg
A3
M4.jpg
B1
M5.jpg
B2
The way i understood your requirement is :
Change all the .jpg file names from what ever name they have to M*.jpg with an incremental number following M.
During multiple runs of the program the count should increase from the highest value of M file that was setup during the last run. For example if the first run of the program created M1, M2, M3 and then you add more files they should start from M4, M5, M6.
But the M1, M2, M3 itself should not be changed.
So a couple of suggestions :
you are setting n=1 at the start of the script. You should use the highest value of Mx.jpg that exists in the file from the previous run.
Second, when you are looking for files and iterating them, exclude all M*.jpg files, so that their names are not replaced.
Do you think this is your requirement. If you have any additional inputs please add a comment. Will send out the script in the next comment.
Test Cases :
Existing Files, X.jpg, ASG.jpg, GER.jpg
File names after the run : M1.jpg, M2.jpg, M3.jpg
If you just run the script once more after the first run with no additional files: There should not be any change in the file names : M1.jpg, M2.jpg, M3.jpg.
Now Add more files : Z.jpg, A.jpg, B.jpg
The file names should be changed as follows :
M1.jpg => M1.jpg, M2.jpg => M2.jpg, M3.jpg => M3.jpg, A.jpg => M4.jpg, B.jpg => M5.jpg, Z.jpg => M6.jpg.
So the file names will be M1.jpg, M2.jpg, M3.jpg, M4.jpg, M5.jpg, M6jpg.