Find operator can't go up in directory? - shell

I'm trying to list all files of a directory that not exist in another.
And the script tells me:
find: ‘ (...) /nanorc/../original/*’: No such file or directory
Where is the problem? Thanks!
Code:
# todo: list all files that isn't in original folder
# get a file in nanorc folder
# try to find it in the ../original folder
# if there isn't, list it.
cd nanorc/
for file in *; do
[ -e "$file" ] || continue
# if [ -z $(find "$(pwd)/../original/*" -name "$file") ]; then
if [ -z $(find $(pwd)/../original/* -name "$file") ]; then
lite=`printf "%s %s" "$lite" "$file"`
fi
done
cd ..
echo "$lite
Notes:
The script must be POSIX complaint.
I tried various variations of find, like: "../original", "../original/*",...
I don't want to create files; the script should populate one variable after the checks.
Solution:
Just put out the "".

To check if a file exists use test -e.
if [ ! -e "../original/$file" ]; then
echo "File ../original/$file does not exists"
fi
Path are by default searched in current working directory, there is no need to use $(pwd).
The error in your script came from "$(pwd)/../original/*" - the * is inside " so it does not expand. You could change it to "../original/"*, but because find works recursively, just find ../original.

Related

Delete/move files which in all respects have same file name except one set has a specific prefix added

I have a large set of working files of the form a.mp4 b.txt c.avi d.doc etc (the extension is irrelevant to the question). I also have a set of files which include the same named files except with a common specific prefix "broken_" eg broken_a.mp4, broken_b.txt
If I have a.mp4 and broken_a.mp4, I want to move the broken_a.mp4 to a holding directory. If I have broken_d.mp4 but no matching d.mp4, then leave it alone.
I have some code successfully used to identify and move files with the same extension which I'd like to modify
This is the form of working example code for same extension files (kudos to the original author) which I'd like to modify if possible to do the job
#!/bin/bash
# Name of source directory
SOURCE_DIR=.
# Name of destination directory
DEST_DIR=already_converted_m4v
# Create the destination directory for the moved files, if it doesn't already exist.
[ ! -d $DEST_DIR ] && mkdir -p $DEST_DIR
find $SOURCE_DIR -maxdepth 1 -type f -iname "*.avi" | while read fin
do
#echo "m4v doing avi"
fm4v=${fin/.avi/.m4v}
[ -f "$fm4v" ] && gmv -v --backup=numbered "$fin" $DEST_DIR/
done
My garbage first attempt which clearly doesnt work looks horribly like:
#!/bin/bash
# Name of source directory
SOURCE_DIR=.
# Name of destination directory
DEST_DIR=Already_broken
# Create the destination directory for the moved files, if it doesn't already exist.
[ ! -d $DEST_DIR ] && mkdir -p $DEST_DIR
find $SOURCE_DIR -maxdepth 1 -type f -iname "*" | while read fin
do
#echo "working to find existing broken and unbroken files"
filetest_basename=$(basename "$fin" )
filetest_extension=$(extension "$fin" )
echo $filetest_basename
echo $filetest_extension
fileok=${filetest_basename/!broken_/broken_}
[ -f "$fileok" ] && gmv -v --backup=numbered "$fin" $DEST_DIR/
done
Grateful for help
find is irrelevant here, a simple shell loop would suffice:
SRCDIR='.'
DSTDIR='Already_broken'
if ! [ -d "$DSTDIR" ]; then
mkdir -p -- "$DSTDIR"
fi
for broken in "$SRCDIR"/broken_*; do
if [ -f "${broken%"${broken##*/}"}${broken##*/broken_}" ]; then
echo gmv -v --backup=numbered "$broken" -- "$DSTDIR"
fi
done
If its output looks good, remove echo.
Some notes:
We can't use ${broken/broken_} here for SRCDIR might contain broken_ in the future.
Nested PE (${broken##*/}) needs to be quoted for its result might contain metacharacters and that would bring about undesired results.

How to iterate over a directory and display only filename

I would want to iterate over contents of a directory and list only ordinary files.
The path of the directory is given as an user input. The script works if the input is current directory but not with others.
I am aware that this can be done using ls.. but i need to use a for .. in control structure.
#!/bin/bash
echo "Enter the path:"
read path
contents=$(ls $path)
for content in $contents
do
if [ -f $content ];
then
echo $content
fi
done
ls is only returning the file names, not including the path. You need to either:
Change your working directory to the path in question, or
Combine the path with the names for your -f test
Option #2 would just change:
if [ -f $content ];
to:
if [ -f "$path/$content" ];
Note that there are other issues here; ls may make changes to the output that break this, depending on wrapping. If you insist on using ls, you can at least make it (somewhat) safer with:
contents="$(command ls -1F "$path")"
You have two ways of doing this properly:
Either loop through the * pattern and test file type:
#!/usr/bin/env bash
echo "Enter the path:"
read -r path
for file in "$path/"*; do
if [ -f "$file" ]; then
echo "$file"
fi
done
Or using find to iterate a null delimited list of file-names:
#!/usr/bin/env bash
echo "Enter the path:"
read -r path
while IFS= read -r -d '' file; do
echo "$file"
done < <(
find "$path" -maxdepth 1 -type f -print0
)
The second way is preferred since it will properly handle files with special characters and offload the file-type check to the find command.
Use file, set to search for files (-type f) from $path directory:
find "$path" -type f
Here is what you could write:
#!/usr/bin/env bash
path=
while [[ ! $path ]]; do
read -p "Enter path: " path
done
for file in "$path"/*; do
[[ -f $file ]] && printf '%s\n' "$file"
done
If you want to traverse all the subdirectories recursively looking for files, you can use globstar:
shopt -s globstar
for file in "$path"/**; do
printf '%s\n' "$file"
done
In case you are looking for specific files based on one or more patterns or some other condition, you could use the find command to pick those files. See this post:
How to loop through file names returned by find?
Related
When to wrap quotes around a shell variable?
Why you shouldn't parse the output of ls
Is double square brackets [[ ]] preferable over single square brackets [ ] in Bash?

Shell Script to list files in a given directory and if they are files or directories

Currently learning some bash scripting and having an issue with a question involving listing all files in a given directory and stating if they are a file or directory. The issue I am having is that I only get either my current directory or if a specify a directory it will just say that it is a directory eg. /home/user/shell_scripts will return shell_scipts is a directory rather than the files contained within it.
This is what I have so far:
dir=$dir
for file in $dir; do
if [[ -d $file ]]; then
echo "$file is a directory"
if [[ -f $file ]]; then
echo "$file is a regular file"
fi
done
Your line:
for file in $dir; do
will expand $dir just to a single directory string. What you need to do is expand that to a list of files in the directory. You could do this using the following:
for file in "${dir}/"* ; do
This will expand the "${dir}/"* section into a name-only list of the current directory. As Biffen points out, this should guarantee that the file list wont end up with split partial file names in file if any of them contain whitespace.
If you want to recurse into the directories in dir then using find might be a better approach. Simply use:
for file in $( find ${dir} ); do
Note that while simple, this will not handle files or directories with spaces in them. Because of this, I would be tempted to drop the loop and generate the output in one go. This might be slightly different than what you want, but is likely to be easier to read and a lot more efficient, especially with large numbers of files. For example, To list all the directories:
find ${dir} -maxdepth 1 -type d
and to list the files:
find ${dir} -maxdepth 1 -type f
if you want to iterate into directories below, then remove the -maxdepth 1
This is a good use for globbing:
for file in "$dir/"*
do
[[ -d "$file" ]] && echo "$file is a directory"
[[ -f "$file" ]] && echo "$file is a regular file"
done
This will work even if files in $dir have special characters in their names, such as spaces, asterisks and even newlines.
Also note that variables should be quoted ("$file"). But * must not be quoted. And I removed dir=$dir since it doesn't do anything (except break when $dir contains special characters).
ls -F ~ | \
sed 's#.*/$#/& is a Directory#;t quit;s#.*#/& is a File#;:quit;s/[*/=>#|] / /'
The -F "classify" switch appends a "/" if a file is a directory. The sed code prints the desired message, then removes the suffix.
for file in $(ls $dir)
do
[ -f $file ] && echo "$file is File"
[ -d $file ] && echo "$file is Directory"
done
or replace the
$(ls $dir)
with
`ls $`
If you want to list files that also start with . use:
for file in "${dir}/"* "${dir}/"/.[!.]* "${dir}/"/..?* ; do

Unix - Shell script to find a file from any directory and move it

i'm currently working through an exercise book and I have to create a shell script that will find a file from any directory and move it.
Though I am having difficulties as the file could be in any directory (so I do not have a path to find it). I have used the find option with the -print flag tho what would be the next step to move it using mv command?
My code so far reads in a variable, detects if a file has been entered, if it is a file or a directory, or if it exists.
The next stage as mentioned above is to find the file and then move it into a "test" file.
If anyone has any recommendations it would be greatly appreciated.
#!/bin/bash
bin="deleted"
if [ ! -e bin ] ; then
mkdir $bin
fi
file=$1
#error to display that no file has been entered
if [[ ! $file ]]; then
echo "no file has been entered"
fi
#file does not exist, display error message
if [[ ! -f $file ]]; then
echo "$file does not exsist!"
fi
#check to see if the input is a directory
if [[ -d $file ]]; then
echo "$file is a directory!"
if [[ -e $file ]]; then *** move to test folder
****This is where I am having the problems
find / -type f -name FILENAME | xargs -I foobar echo mv foobar /tmp (remove echo to make the command actually work .. i put it there just to save yourself from accidentally moving files just to try out the command)
Note that -I foobar means that in mv foobar /tmp replace the foobar string with full path of the file found.
for example, try: find / -type f -name FILENAME | xargs -I foobar foobar is a cool file

bash scripting copying all files in folder

I'm writing a shell script as follows:
for file in `ls`
do
mkdir "$file"_folder
cp $file "$file"_folder
done
What I want to do is to make a folder for each file in the current directory with its name and then underscore folder as the name and then copy that file into it. My problem is that the file names contain spaces in them. How do I escape them?
There are many resources explaining how to do this for variables but none of them can be applied to this situation where I use a for loop to get the names.
Don't use ls there, use shell globbing. (In general, do not parse the output of ls.)
for file in *
do
# only consider files, not directories
if [ -f "$file" ] ; then
new_dir="$file"_folder
# create the directory
if [ ! -d "$new_dir" ] ; then
mkdir "$new_dir"
if [ $? -ne 0 ] ; then
# handle directory creation eror
fi
fi
# possibly check for the copied file existence here
# and deal with that appropriately (i.e. skip/error/copy anyway)
cp "$file" "$new_dir"
fi
done
How about
find . -type f -exec mkdir {}_folder \; -exec cp {} {}_folder \;
It finds all regular files in the current directory, creates the folder (first -exec), and copies the file into the new folder (second -exec).
You do not parse
ls for exactly this reason
for file in *
do
mkdir "${file}_folder"
cp "$file" "${file}_folder"
done

Resources