How can I check if a file can be created or truncated/overwritten 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 either be created or overwritten at some point in the script, like foo.sh file.txt
or foo.sh dir/file.txt
.
The create-or-overwrite behavior is much like the requirements for putting the file on the right side of the >
output redirect operator, or passing it as an argument to tee
(in fact, passing it as an argument to tee
is exactly what I'm doing).
Before I get into the guts of the script, I want to make a reasonable check if the file can be created/overwritten, 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 either be created or overwritten at some point in the script, like foo.sh file.txt
or foo.sh dir/file.txt
.
The create-or-overwrite behavior is much like the requirements for putting the file on the right side of the >
output redirect operator, or passing it as an argument to tee
(in fact, passing it as an argument to tee
is exactly what I'm doing).
Before I get into the guts of the script, I want to make a reasonable check if the file can be created/overwritten, 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
3 hours 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
3 hours 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
2 hours 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
2 hours 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 either be created or overwritten at some point in the script, like foo.sh file.txt
or foo.sh dir/file.txt
.
The create-or-overwrite behavior is much like the requirements for putting the file on the right side of the >
output redirect operator, or passing it as an argument to tee
(in fact, passing it as an argument to tee
is exactly what I'm doing).
Before I get into the guts of the script, I want to make a reasonable check if the file can be created/overwritten, 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 either be created or overwritten at some point in the script, like foo.sh file.txt
or foo.sh dir/file.txt
.
The create-or-overwrite behavior is much like the requirements for putting the file on the right side of the >
output redirect operator, or passing it as an argument to tee
(in fact, passing it as an argument to tee
is exactly what I'm doing).
Before I get into the guts of the script, I want to make a reasonable check if the file can be created/overwritten, 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 20 mins ago
asked 3 hours ago
BeeOnRope
1578
1578
Will the argument always be based on the current directory or could the user specify a full path?
â Jesse_b
3 hours 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
3 hours 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
2 hours 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
2 hours ago
add a comment |Â
Will the argument always be based on the current directory or could the user specify a full path?
â Jesse_b
3 hours 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
3 hours 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
2 hours 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
2 hours ago
Will the argument always be based on the current directory or could the user specify a full path?
â Jesse_b
3 hours ago
Will the argument always be based on the current directory or could the user specify a full path?
â Jesse_b
3 hours 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
3 hours 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
3 hours 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
2 hours 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
2 hours 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
2 hours 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
2 hours ago
add a comment |Â
5 Answers
5
active
oldest
votes
up vote
4
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
2 hours ago
@DopeGhoti that doesnâÂÂt avoid the user getting upset.
â Stephen Kitt
2 hours ago
Hmmm, interesting... I had this idea of usingset -o noclobber
, in which case it would fail if the file already existed... But then I don't think it's possible to differentiate failure due to permissions (abort script) from failure due to file already existing (in which case you'd like to proceed and later overwrite the file.) If this was C or Python, etc., it's just a matter of opening the file with O_EXCL and looking for error EEXIST (permissions are fine, but file already exists) from EACCES (permission denied)... But not sure that can be done in bash...
â Filipe Brandenburger
2 hours ago
add a comment |Â
up vote
1
down vote
From what I'm gathering, you want to check that when using
tee -- "$OUT_FILE"
(note the --
or it wouldn't work for file names that start with -), tee
would succeed to open the file for writing.
That is that:
- the length of the file path doesn't exceed the PATH_MAX limit
- the file exists (after symlink resolution) and is not of type directory and you have write permission to it.
- if the file doesn't exist, the dirname of the file exists (after symlink resolution) as a directory and you have write and search permission to it and the filename length doesn't exceed the NAME_MAX limit of the filesystem that directory resides in.
- or the file is a symlink that points to a file that doesn't exist and is not a symlink loop but meets the criteria just above
We'll ignore for now filesystems like vfat, ntfs or hfsplus that have limitations on what byte values file names may contain, disk quota, process limit, selinux, apparmor or other security mechanism in the way, full filesystem, no inode left, device files that can't be opened that way for a reason or another, files that are executables currently mapped in some process address space all of which could also affect the ability to open or create the file.
With zsh
:
zmodload zsh/system
tee_would_likely_succeed()
local file=$1 ERRNO=0
if [ -d "$file" ]; then
return 1 # directory
elif [ -w "$file" ]; then
return 0 # writable non-directory
elif [ -e "$file" ]; then
return 1 # exists, non-writable
elif [ "$errnos[ERRNO]" != ENOENT ]; then
return 1 # only ENOENT error can be recovered
else
local dir=$file:P:h base=$file:t
[ -d "$dir" ] && # directory
[ -w "$dir" ] && # writable
[ -x "$dir" ] && # and searchable
(($#base <= $(getconf -- NAME_MAX "$dir")))
return
fi
Yes, you understood my intent correctly. In fact, the original question was unclear: I originally spoke about creating the file, but actually I am using it withtee
so the requirement is really that the file can be created if it doesn't exist, or can be truncated to zero and overwritten if it does (or however elsetee
handles that).
â BeeOnRope
17 mins ago
add a comment |Â
up vote
0
down vote
What about using normal test
command like outlined below?
FILE=$1
DIR=$(dirname $FILE) # $DIR now contains '.' for file names only, 'foo' for 'foo/bar'
if [ -d $DIR ] ; then
echo "base directory $DIR for file exists"
if [ -e $FILE ] ; then
if [ -w $FILE ] ; then
echo "file exists, is writeable"
else
echo "file exists, NOT writeable"
fi
elif [ -w $DIR ] ; then
echo "directory is writeable"
else
echo "directory is NOT writeable"
fi
else
echo "can NOT create file in non-existent directory $DIR "
fi
add a comment |Â
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
4
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
4
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
4
down vote
up vote
4
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 3 hours 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 3 hours ago
Jesse_b
10.6k22661
10.6k22661
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
2 hours ago
@DopeGhoti that doesnâÂÂt avoid the user getting upset.
â Stephen Kitt
2 hours ago
Hmmm, interesting... I had this idea of usingset -o noclobber
, in which case it would fail if the file already existed... But then I don't think it's possible to differentiate failure due to permissions (abort script) from failure due to file already existing (in which case you'd like to proceed and later overwrite the file.) If this was C or Python, etc., it's just a matter of opening the file with O_EXCL and looking for error EEXIST (permissions are fine, but file already exists) from EACCES (permission denied)... But not sure that can be done in bash...
â Filipe Brandenburger
2 hours 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
2 hours ago
@DopeGhoti that doesnâÂÂt avoid the user getting upset.
â Stephen Kitt
2 hours ago
Hmmm, interesting... I had this idea of usingset -o noclobber
, in which case it would fail if the file already existed... But then I don't think it's possible to differentiate failure due to permissions (abort script) from failure due to file already existing (in which case you'd like to proceed and later overwrite the file.) If this was C or Python, etc., it's just a matter of opening the file with O_EXCL and looking for error EEXIST (permissions are fine, but file already exists) from EACCES (permission denied)... But not sure that can be done in bash...
â Filipe Brandenburger
2 hours 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 3 hours 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
2 hours ago
@DopeGhoti that doesnâÂÂt avoid the user getting upset.
â Stephen Kitt
2 hours ago
Hmmm, interesting... I had this idea of usingset -o noclobber
, in which case it would fail if the file already existed... But then I don't think it's possible to differentiate failure due to permissions (abort script) from failure due to file already existing (in which case you'd like to proceed and later overwrite the file.) If this was C or Python, etc., it's just a matter of opening the file with O_EXCL and looking for error EEXIST (permissions are fine, but file already exists) from EACCES (permission denied)... But not sure that can be done in bash...
â Filipe Brandenburger
2 hours 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
2 hours ago
@DopeGhoti that doesnâÂÂt avoid the user getting upset.
â Stephen Kitt
2 hours ago
Hmmm, interesting... I had this idea of usingset -o noclobber
, in which case it would fail if the file already existed... But then I don't think it's possible to differentiate failure due to permissions (abort script) from failure due to file already existing (in which case you'd like to proceed and later overwrite the file.) If this was C or Python, etc., it's just a matter of opening the file with O_EXCL and looking for error EEXIST (permissions are fine, but file already exists) from EACCES (permission denied)... But not sure that can be done in bash...
â Filipe Brandenburger
2 hours 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
2 hours 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
2 hours ago
@DopeGhoti that doesnâÂÂt avoid the user getting upset.
â Stephen Kitt
2 hours ago
@DopeGhoti that doesnâÂÂt avoid the user getting upset.
â Stephen Kitt
2 hours ago
Hmmm, interesting... I had this idea of using
set -o noclobber
, in which case it would fail if the file already existed... But then I don't think it's possible to differentiate failure due to permissions (abort script) from failure due to file already existing (in which case you'd like to proceed and later overwrite the file.) If this was C or Python, etc., it's just a matter of opening the file with O_EXCL and looking for error EEXIST (permissions are fine, but file already exists) from EACCES (permission denied)... But not sure that can be done in bash...â Filipe Brandenburger
2 hours ago
Hmmm, interesting... I had this idea of using
set -o noclobber
, in which case it would fail if the file already existed... But then I don't think it's possible to differentiate failure due to permissions (abort script) from failure due to file already existing (in which case you'd like to proceed and later overwrite the file.) If this was C or Python, etc., it's just a matter of opening the file with O_EXCL and looking for error EEXIST (permissions are fine, but file already exists) from EACCES (permission denied)... But not sure that can be done in bash...â Filipe Brandenburger
2 hours ago
add a comment |Â
up vote
1
down vote
From what I'm gathering, you want to check that when using
tee -- "$OUT_FILE"
(note the --
or it wouldn't work for file names that start with -), tee
would succeed to open the file for writing.
That is that:
- the length of the file path doesn't exceed the PATH_MAX limit
- the file exists (after symlink resolution) and is not of type directory and you have write permission to it.
- if the file doesn't exist, the dirname of the file exists (after symlink resolution) as a directory and you have write and search permission to it and the filename length doesn't exceed the NAME_MAX limit of the filesystem that directory resides in.
- or the file is a symlink that points to a file that doesn't exist and is not a symlink loop but meets the criteria just above
We'll ignore for now filesystems like vfat, ntfs or hfsplus that have limitations on what byte values file names may contain, disk quota, process limit, selinux, apparmor or other security mechanism in the way, full filesystem, no inode left, device files that can't be opened that way for a reason or another, files that are executables currently mapped in some process address space all of which could also affect the ability to open or create the file.
With zsh
:
zmodload zsh/system
tee_would_likely_succeed()
local file=$1 ERRNO=0
if [ -d "$file" ]; then
return 1 # directory
elif [ -w "$file" ]; then
return 0 # writable non-directory
elif [ -e "$file" ]; then
return 1 # exists, non-writable
elif [ "$errnos[ERRNO]" != ENOENT ]; then
return 1 # only ENOENT error can be recovered
else
local dir=$file:P:h base=$file:t
[ -d "$dir" ] && # directory
[ -w "$dir" ] && # writable
[ -x "$dir" ] && # and searchable
(($#base <= $(getconf -- NAME_MAX "$dir")))
return
fi
Yes, you understood my intent correctly. In fact, the original question was unclear: I originally spoke about creating the file, but actually I am using it withtee
so the requirement is really that the file can be created if it doesn't exist, or can be truncated to zero and overwritten if it does (or however elsetee
handles that).
â BeeOnRope
17 mins ago
add a comment |Â
up vote
1
down vote
From what I'm gathering, you want to check that when using
tee -- "$OUT_FILE"
(note the --
or it wouldn't work for file names that start with -), tee
would succeed to open the file for writing.
That is that:
- the length of the file path doesn't exceed the PATH_MAX limit
- the file exists (after symlink resolution) and is not of type directory and you have write permission to it.
- if the file doesn't exist, the dirname of the file exists (after symlink resolution) as a directory and you have write and search permission to it and the filename length doesn't exceed the NAME_MAX limit of the filesystem that directory resides in.
- or the file is a symlink that points to a file that doesn't exist and is not a symlink loop but meets the criteria just above
We'll ignore for now filesystems like vfat, ntfs or hfsplus that have limitations on what byte values file names may contain, disk quota, process limit, selinux, apparmor or other security mechanism in the way, full filesystem, no inode left, device files that can't be opened that way for a reason or another, files that are executables currently mapped in some process address space all of which could also affect the ability to open or create the file.
With zsh
:
zmodload zsh/system
tee_would_likely_succeed()
local file=$1 ERRNO=0
if [ -d "$file" ]; then
return 1 # directory
elif [ -w "$file" ]; then
return 0 # writable non-directory
elif [ -e "$file" ]; then
return 1 # exists, non-writable
elif [ "$errnos[ERRNO]" != ENOENT ]; then
return 1 # only ENOENT error can be recovered
else
local dir=$file:P:h base=$file:t
[ -d "$dir" ] && # directory
[ -w "$dir" ] && # writable
[ -x "$dir" ] && # and searchable
(($#base <= $(getconf -- NAME_MAX "$dir")))
return
fi
Yes, you understood my intent correctly. In fact, the original question was unclear: I originally spoke about creating the file, but actually I am using it withtee
so the requirement is really that the file can be created if it doesn't exist, or can be truncated to zero and overwritten if it does (or however elsetee
handles that).
â BeeOnRope
17 mins ago
add a comment |Â
up vote
1
down vote
up vote
1
down vote
From what I'm gathering, you want to check that when using
tee -- "$OUT_FILE"
(note the --
or it wouldn't work for file names that start with -), tee
would succeed to open the file for writing.
That is that:
- the length of the file path doesn't exceed the PATH_MAX limit
- the file exists (after symlink resolution) and is not of type directory and you have write permission to it.
- if the file doesn't exist, the dirname of the file exists (after symlink resolution) as a directory and you have write and search permission to it and the filename length doesn't exceed the NAME_MAX limit of the filesystem that directory resides in.
- or the file is a symlink that points to a file that doesn't exist and is not a symlink loop but meets the criteria just above
We'll ignore for now filesystems like vfat, ntfs or hfsplus that have limitations on what byte values file names may contain, disk quota, process limit, selinux, apparmor or other security mechanism in the way, full filesystem, no inode left, device files that can't be opened that way for a reason or another, files that are executables currently mapped in some process address space all of which could also affect the ability to open or create the file.
With zsh
:
zmodload zsh/system
tee_would_likely_succeed()
local file=$1 ERRNO=0
if [ -d "$file" ]; then
return 1 # directory
elif [ -w "$file" ]; then
return 0 # writable non-directory
elif [ -e "$file" ]; then
return 1 # exists, non-writable
elif [ "$errnos[ERRNO]" != ENOENT ]; then
return 1 # only ENOENT error can be recovered
else
local dir=$file:P:h base=$file:t
[ -d "$dir" ] && # directory
[ -w "$dir" ] && # writable
[ -x "$dir" ] && # and searchable
(($#base <= $(getconf -- NAME_MAX "$dir")))
return
fi
From what I'm gathering, you want to check that when using
tee -- "$OUT_FILE"
(note the --
or it wouldn't work for file names that start with -), tee
would succeed to open the file for writing.
That is that:
- the length of the file path doesn't exceed the PATH_MAX limit
- the file exists (after symlink resolution) and is not of type directory and you have write permission to it.
- if the file doesn't exist, the dirname of the file exists (after symlink resolution) as a directory and you have write and search permission to it and the filename length doesn't exceed the NAME_MAX limit of the filesystem that directory resides in.
- or the file is a symlink that points to a file that doesn't exist and is not a symlink loop but meets the criteria just above
We'll ignore for now filesystems like vfat, ntfs or hfsplus that have limitations on what byte values file names may contain, disk quota, process limit, selinux, apparmor or other security mechanism in the way, full filesystem, no inode left, device files that can't be opened that way for a reason or another, files that are executables currently mapped in some process address space all of which could also affect the ability to open or create the file.
With zsh
:
zmodload zsh/system
tee_would_likely_succeed()
local file=$1 ERRNO=0
if [ -d "$file" ]; then
return 1 # directory
elif [ -w "$file" ]; then
return 0 # writable non-directory
elif [ -e "$file" ]; then
return 1 # exists, non-writable
elif [ "$errnos[ERRNO]" != ENOENT ]; then
return 1 # only ENOENT error can be recovered
else
local dir=$file:P:h base=$file:t
[ -d "$dir" ] && # directory
[ -w "$dir" ] && # writable
[ -x "$dir" ] && # and searchable
(($#base <= $(getconf -- NAME_MAX "$dir")))
return
fi
edited 1 hour ago
answered 1 hour ago
Stéphane Chazelas
292k54544884
292k54544884
Yes, you understood my intent correctly. In fact, the original question was unclear: I originally spoke about creating the file, but actually I am using it withtee
so the requirement is really that the file can be created if it doesn't exist, or can be truncated to zero and overwritten if it does (or however elsetee
handles that).
â BeeOnRope
17 mins ago
add a comment |Â
Yes, you understood my intent correctly. In fact, the original question was unclear: I originally spoke about creating the file, but actually I am using it withtee
so the requirement is really that the file can be created if it doesn't exist, or can be truncated to zero and overwritten if it does (or however elsetee
handles that).
â BeeOnRope
17 mins ago
Yes, you understood my intent correctly. In fact, the original question was unclear: I originally spoke about creating the file, but actually I am using it with
tee
so the requirement is really that the file can be created if it doesn't exist, or can be truncated to zero and overwritten if it does (or however else tee
handles that).â BeeOnRope
17 mins ago
Yes, you understood my intent correctly. In fact, the original question was unclear: I originally spoke about creating the file, but actually I am using it with
tee
so the requirement is really that the file can be created if it doesn't exist, or can be truncated to zero and overwritten if it does (or however else tee
handles that).â BeeOnRope
17 mins ago
add a comment |Â
up vote
0
down vote
What about using normal test
command like outlined below?
FILE=$1
DIR=$(dirname $FILE) # $DIR now contains '.' for file names only, 'foo' for 'foo/bar'
if [ -d $DIR ] ; then
echo "base directory $DIR for file exists"
if [ -e $FILE ] ; then
if [ -w $FILE ] ; then
echo "file exists, is writeable"
else
echo "file exists, NOT writeable"
fi
elif [ -w $DIR ] ; then
echo "directory is writeable"
else
echo "directory is NOT writeable"
fi
else
echo "can NOT create file in non-existent directory $DIR "
fi
add a comment |Â
up vote
0
down vote
What about using normal test
command like outlined below?
FILE=$1
DIR=$(dirname $FILE) # $DIR now contains '.' for file names only, 'foo' for 'foo/bar'
if [ -d $DIR ] ; then
echo "base directory $DIR for file exists"
if [ -e $FILE ] ; then
if [ -w $FILE ] ; then
echo "file exists, is writeable"
else
echo "file exists, NOT writeable"
fi
elif [ -w $DIR ] ; then
echo "directory is writeable"
else
echo "directory is NOT writeable"
fi
else
echo "can NOT create file in non-existent directory $DIR "
fi
add a comment |Â
up vote
0
down vote
up vote
0
down vote
What about using normal test
command like outlined below?
FILE=$1
DIR=$(dirname $FILE) # $DIR now contains '.' for file names only, 'foo' for 'foo/bar'
if [ -d $DIR ] ; then
echo "base directory $DIR for file exists"
if [ -e $FILE ] ; then
if [ -w $FILE ] ; then
echo "file exists, is writeable"
else
echo "file exists, NOT writeable"
fi
elif [ -w $DIR ] ; then
echo "directory is writeable"
else
echo "directory is NOT writeable"
fi
else
echo "can NOT create file in non-existent directory $DIR "
fi
What about using normal test
command like outlined below?
FILE=$1
DIR=$(dirname $FILE) # $DIR now contains '.' for file names only, 'foo' for 'foo/bar'
if [ -d $DIR ] ; then
echo "base directory $DIR for file exists"
if [ -e $FILE ] ; then
if [ -w $FILE ] ; then
echo "file exists, is writeable"
else
echo "file exists, NOT writeable"
fi
elif [ -w $DIR ] ; then
echo "directory is writeable"
else
echo "directory is NOT writeable"
fi
else
echo "can NOT create file in non-existent directory $DIR "
fi
answered 1 hour ago
Jaleks
1,118422
1,118422
add a comment |Â
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-or-truncated-overwritten-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
3 hours 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
3 hours 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
2 hours 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
2 hours ago