bash if then cp as hardlinks - bash

I would like for the following to hardlink all files to destination, except those directories defined. The find piece is working, but it won't copy any files.
#!/bin/sh
tag_select=$1
source=$3
dest="/backup/"
{
if [[ "$1" = "backup" ]]; then
find . -mindepth 1 -maxdepth 1 ! -name "dir1" ! -name "dir2" | while read line
do
cp -lr "$3" "$dest"
done
fi
}
Please note, I do not want to use rysnc as I would like to create hardlinks in the destination. Thank you in advance!

I guess you know why "$2" doesn't appear anywhere, so we will just presume you are correct. You also understand that every file you find source (e.g. "$3") will be linked to $dest no matter what filenames are discovered by find because you make no use of "$line" that you use as your while read line loop variable. It appears from the question, you want to link all files in source in dest (you must confirm this is your intent) If so, find itself is all you need, e.g.
find source -maxdepth 1 ! -name "dir1" ! -name "dir2" -execdir cp -lr '{}' "$dest" \;
which will find all files (and directories) for 1-level and hardlink each of the files in dest. If that wasn't your intent, please let me know and I'm happy to help further. Your original posts was somewhat an opaque pot of shell stew...

Replace your find command with a simple glob; this also has the benefit of working for any valid file name, not just the ones that don't have newlines in them.
#!/bin/sh
tag_select=$1
source=$3
dest="/backup/"
if [ "$1" = "backup" ]; then
for f in "$source"/*; do
case $f in
dir1|dir2) continue ;;
esac
cp -lr "$f" "$dest"
done
fi

try This
#!/bin/sh
tag_select=$1;
source=$2;
dest="/backup/";
if [ "$1" = "backup" ]; then
find $source -mindepth 1 -maxdepth 1 ! -name "dir1" ! -name "dir2" -exec cp -lr {} "$dest" \;
fi
your command should be
./code.sh backup source_folder_path
example
./code.sh backup ~/Desktop
Try below code for only files in the dir
find $source -maxdepth 1 -type f -exec sh -c "ln -f \"\$(realpath {})\" \"$dest\$(basename {})\"" \;
you cant hard link folders.

Related

Rename files in several subdirectories

I want to rename a file present in several subdirectories using bash script.
my files are in folders:
./FolderA/ABCD/ABCD_Something.ctl
./FolderA/EFGH/EFGH_Something.ctl
./FolderA/WXYZ/WXYZ_Something.ctl
I want to rename all of the .ctl file with the same name (name.ctl).
I tried several command using mv or rename but didnt work.
Working from FolderA:
find . -name '*.ctl' -exec rename *.ctl name.ctl '{}' \;
or
for f in ./*/*.ctl; do mv "$f" "${f/*.ctl/name .ctl}"; done
or
for f in $(find . -type f -name '*.ctl'); do mv $f $(echo "$f" | sed 's/*.ctl/name.ctl/'); done
Can you help me using bash?
thanks
You can do this with one line with:
find . -name *.ctl -exec sh -c 'mv "$1" `dirname "$1"`/name.ctl' x {} \;
The x just allows the filename to be positional character 1 rather than 0 which (in my opinion) wrong to use as a parameter.
Try this:
find . -name '*.ctl' | while read f; do
dn=$(dirname "${f}")
# remove the echo after you sanity check the output
echo mv "${f}" "${dn}/name.ctl"
done
find should get all the files you want, dirname will get just the directory name, and mv will perform the rename. You can remove the quotes if you're sure that you'll never have spaces in the names.

Bash: List directories with a type of file, but missing another type of file

I'm new(ish) to using Bash and I'm trying to figure out how to combine a few different things into one script.
I'm looking for file transfers that were interrupted. These folders contain image files (either jpgs or pngs), but are missing another specific file (finished.txt).
Here is what I'm using to find folders with images (from here):
for f in */incoming/ ; do
log_f="${f//\//}"
echo "searching $f"
find "$f" -iname "*jpg*" -o -iname "*png*" > "/output/${log_f}.txt"
echo "$f finished"
done
Then, I'm running this command to find folders that are missing the finished.txt file (from here):
find -mindepth 2 -maxdepth 2 -type d '!' -exec test -e "{}/finished.txt" ';' -print
Is there a way to combine them so I have a list of folders which have jpg or png files, but don't have finished.txt? Also, If I want to add -mtime, where do I put that?
Alternatively, if there's a better/faster way to do this, I'm interested in that too.
Thanks!
From the first pass when you get the files with jpg/png you can get the directory by using dirname. The list of directories can then be used for iterating over and looking for finished.txt file. On finding you can skip the directory if not print it out.
Something as below should do the needful
for i in `find "$f" -iname "*jpg*" -o -iname "*png*" -exec dirname {} \;`
do
ls $i | grep finished >/dev/null
if [ $? -eq 1 ]; then
echo $i
fi
done
Add " | sort | uniq" at the end of find command to perhaps remove the duplicates. Something like
find "$f" -iname "jpg" -o -iname "png" -exec dirname {} \; | sort | uniq

How can I detect whether a symlink is broken in Bash?

I run find and iterate through the results with [ \( -L $F \) ] to collect certain symbolic links.
I am wondering if there is an easy way to determine if the link is broken (points to a non-existent file) in this scenario.
Here is my code:
FILES=`find /target/ | grep -v '\.disabled$' | sort`
for F in $FILES; do
if [ -L $F ]; then
DO THINGS
fi
done
# test if symlink is broken (by seeing if it links to an existing file)
if [ ! -e "$F" ] ; then
# code if the symlink is broken
fi
This should print out links that are broken:
find /target/dir -type l ! -exec test -e {} \; -print
You can also chain in operations to find command, e.g. deleting the broken link:
find /target/dir -type l ! -exec test -e {} \; -exec rm {} \;
this will work if the symlink was pointing to a file or a directory, but now is broken
if [[ -L "$strFile" ]] && [[ ! -a "$strFile" ]];then
echo "'$strFile' is a broken symlink";
fi
This finds all files of type "link", which also resolves to a type "link". ie. a broken symlink
find /target -type l -xtype l
If you don't mind traversing non-broken dir symlinks, to find all orphaned links:
$ find -L /target -type l | while read -r file; do echo $file is orphaned; done
To find all files that are not orphaned links:
$ find -L /target ! -type l
What's wrong with:
file $f | grep 'broken symbolic link'
If it does qualify as a symbolic link, but is „not existing“, its a broken link.
if [[ -h $link && ! -e $link ]] ; then
_info "$link is a BROKEN SYMLINK"
fi
REFERENCE

bash: After testing mtime by following a symlink, I need to delete the symlink itself and not the target file

Right now I have a script that creates symlinks to anything newer than 2 weeks in the public folders into another folder. However, I can't find any good way of getting rid of the stale symlinks individually as opposed to wiping everything out. I need to test the symlink target mtime and if it's older than 2 weeks, delete the symlink itself and not the linked file.
#!/bin/bash
source="/media/public/"
dest="/pool/new/"
if [[ ! -d $dest ]]; then
exit 1
fi
if [ `hostname` == "punk" ] && [ `uname -o` == "GNU/Linux" ]; then
#rm -f $dest/*
find -L $dest -mtime 14 -type f -exec echo "delete symlink: " {} \;
find -L $source -mtime -14 -type f -exec ln -s -t $dest {} \;
fi
Right now the first find command will delete the target as opposed to the symlink.
Use simply
-exec rm {} +
rm will delete the link itself, not the target.

Rename file names in current directory and all subdirectories

I have 4 files with the following names in different directories and subdirectories
tag0.txt, tag1.txt, tag2.txt and tag3.txt
and wish to rename them as tag0a.txt, tag1a.txt ,tag2a.txt and tag3a.txt in all directories and subdirectories.
Could anyone help me out using a shell script?
Cheers
$ shopt -s globstar
$ rename -n 's/\.txt$/a\.txt/' **/*.txt
foo/bar/tag2.txt renamed as foo/bar/tag2a.txt
foo/tag1.txt renamed as foo/tag1a.txt
tag0.txt renamed as tag0a.txt
Remove -n to rename after checking the result - It is the "dry run" option.
This can of course be done with find:
find . -name 'tag?.txt' -type f -exec bash -c 'mv "$1" ${1%.*}a.${1##*.}' -- {} \;
Here is a posix shell script (checked with dash):
visitDir() {
local file
for file in "$1"/*; do
if [ -d "$file" ]; then
visitDir "$file";
else
if [ -f "$file" ] && echo "$file"|grep -q '^.*/tag[0-3]\.txt$'; then
newfile=$(echo $file | sed 's/\.txt/a.txt/')
echo mv "$file" "$newfile"
fi
fi
done
}
visitDir .
If you can use bashisms, just replace the inner IF with:
if [[ -f "$file" && "$file" =~ ^.*/tag[0-3]\.txt$ ]]; then
echo mv "$file" "${file/.txt/a.txt}"
fi
First check that the result is what you expected, then possibly remove the "echo" in front of the mv command.
Using the Perl script version of rename that may be on your system:
find . -name 'tag?.txt' -exec rename 's/\.txt$/a$&/' {} \;
Using the binary executable version of rename:
find . -name 'tag?.txt' -exec rename .txt a.txt {} \;
which changes the first occurrence of ".txt". Since the file names are constrained by the -name argument, that won't be a problem.
Is this good enough?
jcomeau#intrepid:/tmp$ find . -name tag?.txt
./a/tag0.txt
./b/tagb.txt
./c/tag1.txt
./c/d/tag3.txt
jcomeau#intrepid:/tmp$ for txtfile in $(find . -name 'tag?.txt'); do \
mv $txtfile ${txtfile%%.txt}a.txt; done
jcomeau#intrepid:/tmp$ find . -name tag*.txt
./a/tag0a.txt
./b/tagba.txt
./c/d/tag3a.txt
./c/tag1a.txt
Don't actually put the backslash into the command, and if you do, expect a '>' prompt on the next line. I didn't put that into the output to avoid confusion, but I didn't want anybody to have to scroll either.

Resources