Unix - create path of folders and file - linux

I know you can do mkdir to create a directory and touch to create a file, but is there no way to do both operations in one go?
i.e. if I want to do the below when the folder other does not exist:
cp /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt
Error:
cp: cannot create regular file `/my/other/path/here/cpedthing.txt': No such file or directory
Has anyone come up with a function as a workaround for this?

Use && to combine two commands in one shell line:
COMMAND1 && COMMAND2
mkdir -p /my/other/path/here/ && touch /my/other/path/here/cpedthing.txt
Note: Previously I recommended usage of ; to separate the two commands but as pointed out by #trysis it's probably better to use && in most situations because in case COMMAND1 fails COMMAND2 won't be executed either. (Otherwise this might lead to issues you might not have been expecting.)

You need to make all of the parent directories first.
FILE=./base/data/sounds/effects/camera_click.ogg
mkdir -p "$(dirname "$FILE")" && touch "$FILE"
If you want to get creative, you can make a function:
mktouch() {
if [ $# -lt 1 ]; then
echo "Missing argument";
return 1;
fi
for f in "$#"; do
mkdir -p -- "$(dirname -- "$f")"
touch -- "$f"
done
}
And then use it like any other command:
mktouch ./base/data/sounds/effects/camera_click.ogg ./some/other/file

Do it with /usr/bin/install:
install -D /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt
when you don't have a source file:
install -D <(echo 1) /my/other/path/here/cpedthing.txt

This is what I would do:
mkdir -p /my/other/path/here && touch $_/cpredthing.txt
Here, the $_ is a variable that represents the last argument to the previous command that we executed in line.
As always if you want to see what the output might be, you can test it by using the echo command, like so:
echo mkdir -p /code/temp/other/path/here && echo touch $_/cpredthing.txt
Which outputs as:
mkdir -p /code/temp/other/path/here
touch /code/temp/other/path/here/cpredthing.txt
As a bonus, you could write multiple files at once using brace expansion, for example:
mkdir -p /code/temp/other/path/here &&
touch $_/{cpredthing.txt,anotherfile,somescript.sh}
Again, totally testable with echo:
mkdir -p /code/temp/other/path/here
touch /code/temp/other/path/here/cpredthing.txt /code/temp/other/path/here/anotherfile /code/temp/other/path/here/somescript.sh

#!/bin/sh
for f in "$#"; do mkdir -p "$(dirname "$f")"; done
touch "$#"

you can do it in two steps:
mkdir -p /my/other/path/here/
touch /my/other/path/here/cpedthing.txt

as I saw and test in a unix forum this solves the problem
ptouch() {
for p in "$#"; do
_dir="$(dirname -- "$p")"
[ -d "$_dir" ] || mkdir -p -- "$_dir"
touch -- "$p"
done
}

if [ ! -d /my/other ]
then
mkdir /my/other/path/here
cp /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt
fi

no need for if then statements...
you can do it on a single line usign ;
mkdir -p /my/other/path/here;cp /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt
-- or on two lines --
mkdir -p /my/other/path/here
cp /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt
-- the -p prevents error returns if the directory already exists (which is what I came here looking for :))

In the special (but not uncommon) case where you are trying to recreate the same directory hierarchy, cp --parents can be useful.
For example if /my/long contains the source files, and my/other already exists, you can do this:
cd /my/long
cp --parents path/here/thing.txt /my/other

if you want simple with only 1 param snippet :
rm -rf /abs/path/to/file; #prevent cases when old file was a folder
mkdir -p /abs/path/to/file; #make it fist as a dir
rm -rf /abs/path/to/file; #remove the leaf of the dir preserving parents
touch /abs/path/to/file; #create the actual file

Related

Linux shell script to know if the directory (or file) has 777 permission

We give the upmost permission to a file or directory, using this command:
sudo chmod -R 777 directory
Now I want to know if this command is already executed for a given directory.
I know I can use -r for read, -w for write, and -x for execution, in [ test ] blocks.
But I want to know two things:
Is it also a directory?
Does it have those permissions for everyone?
How can I get that info?
Update
Based on #Barmar comment, I came up with this. But it's not working:
if [ stat /Temp | grep -oP "(?<=Access: \()[^)]*" == '' ]; then
echo '/Temp folder has full access'
else
sudo chmod -R 777 /Temp
fi
This command works though:
stat /Temp | grep -oP "(?<=Access: \()[^)]*"
# prints => 0777/drwxrwxrwx
How should I fix the syntax error of my if-else statement?
You don't need to process the output of stat with grep; you can ask stat to only produce the specific information you want. See the man page regarding the --format option. We can for example write:
# ask stat for the file type and mode, and put those values into $1
# and $2
set -- $(stat --format '%F %a' /Temp)
if [[ $1 == directory ]]; then
if [[ $2 == 777 ]]; then
echo "/Temp folder has full access"
else
sudo chmod -R 777 /Temp
fi
else
echo "ERROR: /Temp is not a directory!" >&2
fi
A simple example:
#!/bin/bash
function setfullperm(){
[ -d $1 ] && \
(
[ "$(stat --format '%a' $1)" == "777" ] && \
echo "Full permissions are applied." || \
( echo "Setting full permissions" && sudo chmod -R 777 $1 )
) || \
( echo "$1 is not a directory !" && mkdir $1 && setfullperm $1 )
}
export setfullperm
Source the script:
$ source example.sh
Set full permissions (777) on any directory, it tests if the directory exists in the first place, if not it will create it and set the permissions.
It will export the function setfullperm to the shell so you can run it:
>$ setfullperm ali
ali is not a directory !
mkdir: created directory 'ali'
Setting full permissions
>$ setfullperm ali
Full permissions are applied.
If using zsh (But not other shells), you can do it with just a glob pattern:
setopt extended_glob null_glob
if [[ -n /Temp(#q/f777) ]]; then
echo '/Temp folder has full access'
else
sudo chmod -R 777 /Temp
fi
The pattern /Temp(#q/f777) will, with the null_glob and extended_glob options set, expand to an empty string if /Temp is anything but a directory with the exact octal permissions 0777 (And to /Temp if the criteria are met). For more details, see Glob Qualifiers in the zsh manual.
I don't recommend using stat for this. Though widespread, stat isn't POSIX, which means there's no guarantee that your script will work in the future or work on other platforms. If you're writing scripts for a production environment, I'd urge you to consider a different approach.
You're better off using ls(1)'s -l option and passing the file as an argument. From there you can use cut(1)'s -c option to grab the file mode flags.
Get file type:
ls -l <file> | cut -c1
Also, don't forget about test's -d operator, which tests if a file is a directory.
Get owner permissions:
ls -l <file> | cut -c2-4
and so on.
This approach is POSIX compliant and it avoids the shortcomings of using stat.

Trying with piping commands into an if statement

I have a bash script that puts a bunch of commands to make a directory into a text file. Then it cats the file into sh to run the commands. What I am trying to do is only run the command if the directory doesn't already exist.
Here is what I have:
A text file with something like this:
mkdir /path/to/a/directory
mkdir /path/to/another/directory
mkdir /path/to/yet/another/directory
In my script I have a line like this
cat /path/to/my/file.txt | sh
But is there a way to do something like this?
cat /path/to/my/file.txt | if path already exists then go to the next, if not | sh
In other words I would like to skip the attempt to make the directory if the path already exists.
Update: The OP has since clarified that use of mkdir is just an example, and that he needs a generic mechanism to conditionally execute lines from a text file containing shell commands, based on whether the commands refers to an existing directory or not:
while read -r cmd dir; do [[ -d $dir ]] || eval "$cmd $path"; done < /path/to/my/file.txt
The while loop reads the text file containing the shell commands line by line.
read -r cmd dir parses each line into the first token - assumed to be the command (mkdir in the sample input) - and the rest, assumed to be the directory path.
[[ -d $dir ]] tests the existence of the directory path, and || only executes its RHS if the test fails, i.e., if the directory does not exist.
eval "$cmd $path" then executes the line; note that use of eval here is not any less secure than piping to sh - in both cases you must trust the strings representing the commands. (Using eval from the current Bash shell means that Bash will execute the command, not sh, but I'm assuming that's not a problem.)
Original answer, based on the assumption that mkdir is actually used:
The simplest approach in your case is to add the -p option to your mkdir calls, which will quietly ignore attempts to create a directory that already exists:
mkdir -p /path/to/a/directory
mkdir -p /path/to/another/directory
mkdir -p /path/to/yet/another/directory
To put it differently: mkdir -p ensures existence of the target dir., whether that dir. already exists or has to be created.
(mkdir -p can still fail, such as when the target path is a file rather than a dir., or if you have insufficient permissions to create the dir.)
You can then simply pass the file to sh (no need for cat and a pipe, which is less efficient):
sh /path/to/my/file.txt
In case you do not control creation of the input file, you can use sed to insert the -p option:
sed 's/^mkdir /&-p /' /path/to/my/file.txt | sh
I'm not clear if you want to check for the existence of files or directories.. but here's how to to it:
Run your command if the file exists:
[ -f /path/to/my/file.txt ] && cat /path/to/my/file.txt | sh
or to check for directories:
[ -d /path/to/my/directory ] && cat /path/to/my/file.txt | sh
Write your own mkdir function.
Assuming your file doesn't use mkdir -p anywhere this should work.
mkdir() {
for dir; do
[ -d "$dir" ] || mkdir "$dir"
done
}
export -f mkdir
sh < file

Create a nested file while redirecting output

In linux/OSx/etc, how can we redirect output to file inside a dir, where even dir needs to be created by this redirection.
basically, how to make sure that below command creates "dir1" and then sends output to "file1" inside "dir1"
echo "abc" > dir1/file1
You cannot do it with one command, you have to use mkdir:
filename="dir1/file1"
mkdir -p "$(dirname "$filename")" && echo "abc" > "$filename"
You can it with this one liner -
if [ ! -d "dir1" ]; then mkdir "dir1"; fi && echo "abc" > dir1/file1

How to make a file and its parent directory at once? [duplicate]

I know you can do mkdir to create a directory and touch to create a file, but is there no way to do both operations in one go?
i.e. if I want to do the below when the folder other does not exist:
cp /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt
Error:
cp: cannot create regular file `/my/other/path/here/cpedthing.txt': No such file or directory
Has anyone come up with a function as a workaround for this?
Use && to combine two commands in one shell line:
COMMAND1 && COMMAND2
mkdir -p /my/other/path/here/ && touch /my/other/path/here/cpedthing.txt
Note: Previously I recommended usage of ; to separate the two commands but as pointed out by #trysis it's probably better to use && in most situations because in case COMMAND1 fails COMMAND2 won't be executed either. (Otherwise this might lead to issues you might not have been expecting.)
You need to make all of the parent directories first.
FILE=./base/data/sounds/effects/camera_click.ogg
mkdir -p "$(dirname "$FILE")" && touch "$FILE"
If you want to get creative, you can make a function:
mktouch() {
if [ $# -lt 1 ]; then
echo "Missing argument";
return 1;
fi
for f in "$#"; do
mkdir -p -- "$(dirname -- "$f")"
touch -- "$f"
done
}
And then use it like any other command:
mktouch ./base/data/sounds/effects/camera_click.ogg ./some/other/file
Do it with /usr/bin/install:
install -D /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt
when you don't have a source file:
install -D <(echo 1) /my/other/path/here/cpedthing.txt
This is what I would do:
mkdir -p /my/other/path/here && touch $_/cpredthing.txt
Here, the $_ is a variable that represents the last argument to the previous command that we executed in line.
As always if you want to see what the output might be, you can test it by using the echo command, like so:
echo mkdir -p /code/temp/other/path/here && echo touch $_/cpredthing.txt
Which outputs as:
mkdir -p /code/temp/other/path/here
touch /code/temp/other/path/here/cpredthing.txt
As a bonus, you could write multiple files at once using brace expansion, for example:
mkdir -p /code/temp/other/path/here &&
touch $_/{cpredthing.txt,anotherfile,somescript.sh}
Again, totally testable with echo:
mkdir -p /code/temp/other/path/here
touch /code/temp/other/path/here/cpredthing.txt /code/temp/other/path/here/anotherfile /code/temp/other/path/here/somescript.sh
#!/bin/sh
for f in "$#"; do mkdir -p "$(dirname "$f")"; done
touch "$#"
you can do it in two steps:
mkdir -p /my/other/path/here/
touch /my/other/path/here/cpedthing.txt
as I saw and test in a unix forum this solves the problem
ptouch() {
for p in "$#"; do
_dir="$(dirname -- "$p")"
[ -d "$_dir" ] || mkdir -p -- "$_dir"
touch -- "$p"
done
}
if [ ! -d /my/other ]
then
mkdir /my/other/path/here
cp /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt
fi
no need for if then statements...
you can do it on a single line usign ;
mkdir -p /my/other/path/here;cp /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt
-- or on two lines --
mkdir -p /my/other/path/here
cp /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt
-- the -p prevents error returns if the directory already exists (which is what I came here looking for :))
In the special (but not uncommon) case where you are trying to recreate the same directory hierarchy, cp --parents can be useful.
For example if /my/long contains the source files, and my/other already exists, you can do this:
cd /my/long
cp --parents path/here/thing.txt /my/other
if you want simple with only 1 param snippet :
rm -rf /abs/path/to/file; #prevent cases when old file was a folder
mkdir -p /abs/path/to/file; #make it fist as a dir
rm -rf /abs/path/to/file; #remove the leaf of the dir preserving parents
touch /abs/path/to/file; #create the actual file

Linux, how to create file using directory name?

How to create folders like: wdw/1/11, wdw/2/22, ... wdw/6/66, ..., wdw/9/99, and file using directory name in the deepest directory like directoryname_file.txt
In bash:
mkdir wdw # Creates the top dir.
mkdir {1..9} # Creates the subdirs using brace expansion.
for dir in {1..9} ; do
mkdir $dir/$dir$dir # Creates the subsubdirs.
touch $dir/$dir$dir/$dirdir"_file".txt # Creates the file.
done
In Bash, you can use the mkdir command:
your_dir="wdw/1/11"
if [ ! -d $your_dir ]; then
mkdir $your_dir
fi
The IF clause is to check if the directory already exists.
You can loop to change the value of "your_dir" with /2/22, 6/66, etc...
You'll have use the -p flag with mkdir to create nested directories, for example:
dir_name="wdw/1/11"
mkdir -p $dir_name
Then use touch or echo to create the files:
touch $dir_name/directoryname_file.txt
or
echo "some text" > $dir_name/directoryname_file.txt

Resources