How can I check if a file can be created in bash?
Clash Royale CLAN TAG#URR8PPP
up vote
2
down vote
favorite
The user calls my script with a file path that will be created at some point in the script, like foo.sh file.txt
or foo.sh dir/file.txt
.
Before I get into the guts of the script, I want to make a reasonable check if the file can be created, but not actually create it. This check doesn't have to be perfect, and yes I realize that the situation can change between the check and the point where the file is actually written - but here I'm OK with a best effort type solution so I can bail out early in the case that the file path is invalid.
Examples of reasons the file couldn't created:
- the file contains a directory component, like
dir/file.txt
but the directorydir
doesn't exist - the user doens't have write permissions in the specified directory (or the CWD if no directory was specified
Yes, I realize that checking permissions "up front" is no the UNIX way, rather I should just try the operation and ask forgiveness later. In my particular script however, this leads to a bad user experience and I can't change the responsible component.
bash files error-handling
add a comment |Â
up vote
2
down vote
favorite
The user calls my script with a file path that will be created at some point in the script, like foo.sh file.txt
or foo.sh dir/file.txt
.
Before I get into the guts of the script, I want to make a reasonable check if the file can be created, but not actually create it. This check doesn't have to be perfect, and yes I realize that the situation can change between the check and the point where the file is actually written - but here I'm OK with a best effort type solution so I can bail out early in the case that the file path is invalid.
Examples of reasons the file couldn't created:
- the file contains a directory component, like
dir/file.txt
but the directorydir
doesn't exist - the user doens't have write permissions in the specified directory (or the CWD if no directory was specified
Yes, I realize that checking permissions "up front" is no the UNIX way, rather I should just try the operation and ask forgiveness later. In my particular script however, this leads to a bad user experience and I can't change the responsible component.
bash files error-handling
Will the argument always be based on the current directory or could the user specify a full path?
â Jesse_b
43 mins ago
@Jesse_b - I suppose the user could specify an absolute path like/foo/bar/file.txt
. Basically I pass the path totee
liketee $OUT_FILE
whereOUT_FILE
is passed on the command line. That should "just work" with both absolute and relative paths, right?
â BeeOnRope
41 mins ago
@BeeOnRope, no you'd needtee -- "$OUT_FILE"
at least. What if the file already exists or exists but is not a regular file (directory, symlink, fifo)?
â Stéphane Chazelas
13 mins ago
@StéphaneChazelas - well I am usingtee "$OUT_FILE.tmp"
. If the file already exists,tee
overwrites, which is the desired behavior in this case. If it's a directory,tee
will fail (I think). symlink I'm not 100% sure?
â BeeOnRope
11 mins ago
add a comment |Â
up vote
2
down vote
favorite
up vote
2
down vote
favorite
The user calls my script with a file path that will be created at some point in the script, like foo.sh file.txt
or foo.sh dir/file.txt
.
Before I get into the guts of the script, I want to make a reasonable check if the file can be created, but not actually create it. This check doesn't have to be perfect, and yes I realize that the situation can change between the check and the point where the file is actually written - but here I'm OK with a best effort type solution so I can bail out early in the case that the file path is invalid.
Examples of reasons the file couldn't created:
- the file contains a directory component, like
dir/file.txt
but the directorydir
doesn't exist - the user doens't have write permissions in the specified directory (or the CWD if no directory was specified
Yes, I realize that checking permissions "up front" is no the UNIX way, rather I should just try the operation and ask forgiveness later. In my particular script however, this leads to a bad user experience and I can't change the responsible component.
bash files error-handling
The user calls my script with a file path that will be created at some point in the script, like foo.sh file.txt
or foo.sh dir/file.txt
.
Before I get into the guts of the script, I want to make a reasonable check if the file can be created, but not actually create it. This check doesn't have to be perfect, and yes I realize that the situation can change between the check and the point where the file is actually written - but here I'm OK with a best effort type solution so I can bail out early in the case that the file path is invalid.
Examples of reasons the file couldn't created:
- the file contains a directory component, like
dir/file.txt
but the directorydir
doesn't exist - the user doens't have write permissions in the specified directory (or the CWD if no directory was specified
Yes, I realize that checking permissions "up front" is no the UNIX way, rather I should just try the operation and ask forgiveness later. In my particular script however, this leads to a bad user experience and I can't change the responsible component.
bash files error-handling
bash files error-handling
edited 30 mins ago
asked 49 mins ago
BeeOnRope
1576
1576
Will the argument always be based on the current directory or could the user specify a full path?
â Jesse_b
43 mins ago
@Jesse_b - I suppose the user could specify an absolute path like/foo/bar/file.txt
. Basically I pass the path totee
liketee $OUT_FILE
whereOUT_FILE
is passed on the command line. That should "just work" with both absolute and relative paths, right?
â BeeOnRope
41 mins ago
@BeeOnRope, no you'd needtee -- "$OUT_FILE"
at least. What if the file already exists or exists but is not a regular file (directory, symlink, fifo)?
â Stéphane Chazelas
13 mins ago
@StéphaneChazelas - well I am usingtee "$OUT_FILE.tmp"
. If the file already exists,tee
overwrites, which is the desired behavior in this case. If it's a directory,tee
will fail (I think). symlink I'm not 100% sure?
â BeeOnRope
11 mins ago
add a comment |Â
Will the argument always be based on the current directory or could the user specify a full path?
â Jesse_b
43 mins ago
@Jesse_b - I suppose the user could specify an absolute path like/foo/bar/file.txt
. Basically I pass the path totee
liketee $OUT_FILE
whereOUT_FILE
is passed on the command line. That should "just work" with both absolute and relative paths, right?
â BeeOnRope
41 mins ago
@BeeOnRope, no you'd needtee -- "$OUT_FILE"
at least. What if the file already exists or exists but is not a regular file (directory, symlink, fifo)?
â Stéphane Chazelas
13 mins ago
@StéphaneChazelas - well I am usingtee "$OUT_FILE.tmp"
. If the file already exists,tee
overwrites, which is the desired behavior in this case. If it's a directory,tee
will fail (I think). symlink I'm not 100% sure?
â BeeOnRope
11 mins ago
Will the argument always be based on the current directory or could the user specify a full path?
â Jesse_b
43 mins ago
Will the argument always be based on the current directory or could the user specify a full path?
â Jesse_b
43 mins ago
@Jesse_b - I suppose the user could specify an absolute path like
/foo/bar/file.txt
. Basically I pass the path to tee
like tee $OUT_FILE
where OUT_FILE
is passed on the command line. That should "just work" with both absolute and relative paths, right?â BeeOnRope
41 mins ago
@Jesse_b - I suppose the user could specify an absolute path like
/foo/bar/file.txt
. Basically I pass the path to tee
like tee $OUT_FILE
where OUT_FILE
is passed on the command line. That should "just work" with both absolute and relative paths, right?â BeeOnRope
41 mins ago
@BeeOnRope, no you'd need
tee -- "$OUT_FILE"
at least. What if the file already exists or exists but is not a regular file (directory, symlink, fifo)?â Stéphane Chazelas
13 mins ago
@BeeOnRope, no you'd need
tee -- "$OUT_FILE"
at least. What if the file already exists or exists but is not a regular file (directory, symlink, fifo)?â Stéphane Chazelas
13 mins ago
@StéphaneChazelas - well I am using
tee "$OUT_FILE.tmp"
. If the file already exists, tee
overwrites, which is the desired behavior in this case. If it's a directory, tee
will fail (I think). symlink I'm not 100% sure?â BeeOnRope
11 mins ago
@StéphaneChazelas - well I am using
tee "$OUT_FILE.tmp"
. If the file already exists, tee
overwrites, which is the desired behavior in this case. If it's a directory, tee
will fail (I think). symlink I'm not 100% sure?â BeeOnRope
11 mins ago
add a comment |Â
3 Answers
3
active
oldest
votes
up vote
3
down vote
The obvious test would be:
if touch /path/to/file; then
: it can be created
fi
But it does actually create the file if it's not already there. We could clean up after ourselves:
if touch /path/to/file; then
rm /path/to/file
fi
But this would remove a file that already existed, which you probably don't want.
We do, however, have a way around this:
if mkdir /path/to/file; then
rmdir /path/to/file
fi
You can't have a directory with the same name as another object in that directory. I can't think of a situation in which you'd be able to create a directory but not create a file. After this test, your script would be free to create a conventional /path/to/file
and do whatever it pleases with it.
add a comment |Â
up vote
1
down vote
I think DopeGhoti's solution is better but this should also work:
file=$1
if [[ "$file:0:1" == '/' ]]; then
dir=$file%/*
elif [[ "$file" =~ .*/.* ]]; then
dir="$(PWD)/$file%/*"
else
dir=$(PWD)
fi
if [[ -w "$dir" ]]; then
echo "writable"
#do stuff with writable file
else
echo "not writable"
#do stuff without writable file
fi
The first if construct checks if the argument is a full path (starts with /
) and sets the dir
variable to the directory path up to the last /
. Otherwise if the argument does not start with a /
but does contain a /
(specifying a sub directory) it will set dir
to the present working directory + the sub directory path. Otherwise it assumes the present working directory. It then checks if that directory is writable.
add a comment |Â
up vote
1
down vote
One option you might want to consider is creating the file early on but only populating it later in your script. You can use the exec
command to open the file in a file descriptor (such as 3, 4, etc.) and then later use a redirection to a file descriptor (>&3
, etc.) to write contents to that file.
Something like:
exec 3>dir/file.txt ||
echo "Error creating dir/file.txt" >&2
exit 1
# long checks here
check_ok ||
echo "Failed checks" >&2
# cleanup file before bailing out
rm -f dir/file.txt
exit 1
# now populate the file, use a redirection to write
# to the previously opened file descriptor
populate_contents >&3
You can also use a trap
to clean up the file on error, that's a common practice.
This way, you get a real check for permissions that you'll be able to create the file, while at the same time being able to perform it early enough that if that fails you haven't spent time waiting for the long checks.
I had considered this, but the problem is that if another innocent error occurs somewhere between when I create the file and the point some time later where I would write to it, the user will probably be upset to find out that the specified file was overwritten.
â BeeOnRope
16 mins ago
@DopeGhoti that doesnâÂÂt avoid the user getting upset.
â Stephen Kitt
5 mins ago
add a comment |Â
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
The obvious test would be:
if touch /path/to/file; then
: it can be created
fi
But it does actually create the file if it's not already there. We could clean up after ourselves:
if touch /path/to/file; then
rm /path/to/file
fi
But this would remove a file that already existed, which you probably don't want.
We do, however, have a way around this:
if mkdir /path/to/file; then
rmdir /path/to/file
fi
You can't have a directory with the same name as another object in that directory. I can't think of a situation in which you'd be able to create a directory but not create a file. After this test, your script would be free to create a conventional /path/to/file
and do whatever it pleases with it.
add a comment |Â
up vote
3
down vote
The obvious test would be:
if touch /path/to/file; then
: it can be created
fi
But it does actually create the file if it's not already there. We could clean up after ourselves:
if touch /path/to/file; then
rm /path/to/file
fi
But this would remove a file that already existed, which you probably don't want.
We do, however, have a way around this:
if mkdir /path/to/file; then
rmdir /path/to/file
fi
You can't have a directory with the same name as another object in that directory. I can't think of a situation in which you'd be able to create a directory but not create a file. After this test, your script would be free to create a conventional /path/to/file
and do whatever it pleases with it.
add a comment |Â
up vote
3
down vote
up vote
3
down vote
The obvious test would be:
if touch /path/to/file; then
: it can be created
fi
But it does actually create the file if it's not already there. We could clean up after ourselves:
if touch /path/to/file; then
rm /path/to/file
fi
But this would remove a file that already existed, which you probably don't want.
We do, however, have a way around this:
if mkdir /path/to/file; then
rmdir /path/to/file
fi
You can't have a directory with the same name as another object in that directory. I can't think of a situation in which you'd be able to create a directory but not create a file. After this test, your script would be free to create a conventional /path/to/file
and do whatever it pleases with it.
The obvious test would be:
if touch /path/to/file; then
: it can be created
fi
But it does actually create the file if it's not already there. We could clean up after ourselves:
if touch /path/to/file; then
rm /path/to/file
fi
But this would remove a file that already existed, which you probably don't want.
We do, however, have a way around this:
if mkdir /path/to/file; then
rmdir /path/to/file
fi
You can't have a directory with the same name as another object in that directory. I can't think of a situation in which you'd be able to create a directory but not create a file. After this test, your script would be free to create a conventional /path/to/file
and do whatever it pleases with it.
answered 39 mins ago
DopeGhoti
42.2k55180
42.2k55180
add a comment |Â
add a comment |Â
up vote
1
down vote
I think DopeGhoti's solution is better but this should also work:
file=$1
if [[ "$file:0:1" == '/' ]]; then
dir=$file%/*
elif [[ "$file" =~ .*/.* ]]; then
dir="$(PWD)/$file%/*"
else
dir=$(PWD)
fi
if [[ -w "$dir" ]]; then
echo "writable"
#do stuff with writable file
else
echo "not writable"
#do stuff without writable file
fi
The first if construct checks if the argument is a full path (starts with /
) and sets the dir
variable to the directory path up to the last /
. Otherwise if the argument does not start with a /
but does contain a /
(specifying a sub directory) it will set dir
to the present working directory + the sub directory path. Otherwise it assumes the present working directory. It then checks if that directory is writable.
add a comment |Â
up vote
1
down vote
I think DopeGhoti's solution is better but this should also work:
file=$1
if [[ "$file:0:1" == '/' ]]; then
dir=$file%/*
elif [[ "$file" =~ .*/.* ]]; then
dir="$(PWD)/$file%/*"
else
dir=$(PWD)
fi
if [[ -w "$dir" ]]; then
echo "writable"
#do stuff with writable file
else
echo "not writable"
#do stuff without writable file
fi
The first if construct checks if the argument is a full path (starts with /
) and sets the dir
variable to the directory path up to the last /
. Otherwise if the argument does not start with a /
but does contain a /
(specifying a sub directory) it will set dir
to the present working directory + the sub directory path. Otherwise it assumes the present working directory. It then checks if that directory is writable.
add a comment |Â
up vote
1
down vote
up vote
1
down vote
I think DopeGhoti's solution is better but this should also work:
file=$1
if [[ "$file:0:1" == '/' ]]; then
dir=$file%/*
elif [[ "$file" =~ .*/.* ]]; then
dir="$(PWD)/$file%/*"
else
dir=$(PWD)
fi
if [[ -w "$dir" ]]; then
echo "writable"
#do stuff with writable file
else
echo "not writable"
#do stuff without writable file
fi
The first if construct checks if the argument is a full path (starts with /
) and sets the dir
variable to the directory path up to the last /
. Otherwise if the argument does not start with a /
but does contain a /
(specifying a sub directory) it will set dir
to the present working directory + the sub directory path. Otherwise it assumes the present working directory. It then checks if that directory is writable.
I think DopeGhoti's solution is better but this should also work:
file=$1
if [[ "$file:0:1" == '/' ]]; then
dir=$file%/*
elif [[ "$file" =~ .*/.* ]]; then
dir="$(PWD)/$file%/*"
else
dir=$(PWD)
fi
if [[ -w "$dir" ]]; then
echo "writable"
#do stuff with writable file
else
echo "not writable"
#do stuff without writable file
fi
The first if construct checks if the argument is a full path (starts with /
) and sets the dir
variable to the directory path up to the last /
. Otherwise if the argument does not start with a /
but does contain a /
(specifying a sub directory) it will set dir
to the present working directory + the sub directory path. Otherwise it assumes the present working directory. It then checks if that directory is writable.
answered 32 mins ago
Jesse_b
10.5k22661
10.5k22661
add a comment |Â
add a comment |Â
up vote
1
down vote
One option you might want to consider is creating the file early on but only populating it later in your script. You can use the exec
command to open the file in a file descriptor (such as 3, 4, etc.) and then later use a redirection to a file descriptor (>&3
, etc.) to write contents to that file.
Something like:
exec 3>dir/file.txt ||
echo "Error creating dir/file.txt" >&2
exit 1
# long checks here
check_ok ||
echo "Failed checks" >&2
# cleanup file before bailing out
rm -f dir/file.txt
exit 1
# now populate the file, use a redirection to write
# to the previously opened file descriptor
populate_contents >&3
You can also use a trap
to clean up the file on error, that's a common practice.
This way, you get a real check for permissions that you'll be able to create the file, while at the same time being able to perform it early enough that if that fails you haven't spent time waiting for the long checks.
I had considered this, but the problem is that if another innocent error occurs somewhere between when I create the file and the point some time later where I would write to it, the user will probably be upset to find out that the specified file was overwritten.
â BeeOnRope
16 mins ago
@DopeGhoti that doesnâÂÂt avoid the user getting upset.
â Stephen Kitt
5 mins ago
add a comment |Â
up vote
1
down vote
One option you might want to consider is creating the file early on but only populating it later in your script. You can use the exec
command to open the file in a file descriptor (such as 3, 4, etc.) and then later use a redirection to a file descriptor (>&3
, etc.) to write contents to that file.
Something like:
exec 3>dir/file.txt ||
echo "Error creating dir/file.txt" >&2
exit 1
# long checks here
check_ok ||
echo "Failed checks" >&2
# cleanup file before bailing out
rm -f dir/file.txt
exit 1
# now populate the file, use a redirection to write
# to the previously opened file descriptor
populate_contents >&3
You can also use a trap
to clean up the file on error, that's a common practice.
This way, you get a real check for permissions that you'll be able to create the file, while at the same time being able to perform it early enough that if that fails you haven't spent time waiting for the long checks.
I had considered this, but the problem is that if another innocent error occurs somewhere between when I create the file and the point some time later where I would write to it, the user will probably be upset to find out that the specified file was overwritten.
â BeeOnRope
16 mins ago
@DopeGhoti that doesnâÂÂt avoid the user getting upset.
â Stephen Kitt
5 mins ago
add a comment |Â
up vote
1
down vote
up vote
1
down vote
One option you might want to consider is creating the file early on but only populating it later in your script. You can use the exec
command to open the file in a file descriptor (such as 3, 4, etc.) and then later use a redirection to a file descriptor (>&3
, etc.) to write contents to that file.
Something like:
exec 3>dir/file.txt ||
echo "Error creating dir/file.txt" >&2
exit 1
# long checks here
check_ok ||
echo "Failed checks" >&2
# cleanup file before bailing out
rm -f dir/file.txt
exit 1
# now populate the file, use a redirection to write
# to the previously opened file descriptor
populate_contents >&3
You can also use a trap
to clean up the file on error, that's a common practice.
This way, you get a real check for permissions that you'll be able to create the file, while at the same time being able to perform it early enough that if that fails you haven't spent time waiting for the long checks.
One option you might want to consider is creating the file early on but only populating it later in your script. You can use the exec
command to open the file in a file descriptor (such as 3, 4, etc.) and then later use a redirection to a file descriptor (>&3
, etc.) to write contents to that file.
Something like:
exec 3>dir/file.txt ||
echo "Error creating dir/file.txt" >&2
exit 1
# long checks here
check_ok ||
echo "Failed checks" >&2
# cleanup file before bailing out
rm -f dir/file.txt
exit 1
# now populate the file, use a redirection to write
# to the previously opened file descriptor
populate_contents >&3
You can also use a trap
to clean up the file on error, that's a common practice.
This way, you get a real check for permissions that you'll be able to create the file, while at the same time being able to perform it early enough that if that fails you haven't spent time waiting for the long checks.
answered 20 mins ago
Filipe Brandenburger
5,0901624
5,0901624
I had considered this, but the problem is that if another innocent error occurs somewhere between when I create the file and the point some time later where I would write to it, the user will probably be upset to find out that the specified file was overwritten.
â BeeOnRope
16 mins ago
@DopeGhoti that doesnâÂÂt avoid the user getting upset.
â Stephen Kitt
5 mins ago
add a comment |Â
I had considered this, but the problem is that if another innocent error occurs somewhere between when I create the file and the point some time later where I would write to it, the user will probably be upset to find out that the specified file was overwritten.
â BeeOnRope
16 mins ago
@DopeGhoti that doesnâÂÂt avoid the user getting upset.
â Stephen Kitt
5 mins ago
I had considered this, but the problem is that if another innocent error occurs somewhere between when I create the file and the point some time later where I would write to it, the user will probably be upset to find out that the specified file was overwritten.
â BeeOnRope
16 mins ago
I had considered this, but the problem is that if another innocent error occurs somewhere between when I create the file and the point some time later where I would write to it, the user will probably be upset to find out that the specified file was overwritten.
â BeeOnRope
16 mins ago
@DopeGhoti that doesnâÂÂt avoid the user getting upset.
â Stephen Kitt
5 mins ago
@DopeGhoti that doesnâÂÂt avoid the user getting upset.
â Stephen Kitt
5 mins ago
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f480656%2fhow-can-i-check-if-a-file-can-be-created-in-bash%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Will the argument always be based on the current directory or could the user specify a full path?
â Jesse_b
43 mins ago
@Jesse_b - I suppose the user could specify an absolute path like
/foo/bar/file.txt
. Basically I pass the path totee
liketee $OUT_FILE
whereOUT_FILE
is passed on the command line. That should "just work" with both absolute and relative paths, right?â BeeOnRope
41 mins ago
@BeeOnRope, no you'd need
tee -- "$OUT_FILE"
at least. What if the file already exists or exists but is not a regular file (directory, symlink, fifo)?â Stéphane Chazelas
13 mins ago
@StéphaneChazelas - well I am using
tee "$OUT_FILE.tmp"
. If the file already exists,tee
overwrites, which is the desired behavior in this case. If it's a directory,tee
will fail (I think). symlink I'm not 100% sure?â BeeOnRope
11 mins ago