Performing -nt/-ot test in a POSIX sh

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
10
down vote

favorite












The built-in test and [ utilities have the -nt ("newer than") and -ot ("older than") tests in most shells, even when the shell is running in "POSIX mode" (also true for the external utilities of the same names on the systems that I have access to). These tests are for comparing modification timestamps on two files. Their documented semantics are slightly varying across implementations (with regards to what happens if one or the other file exists on not), but they are not included in the POSIX spec. for the test utility.




They were not carried forward into the test utility when the conditional command was removed from the [KornShell] shell because they have not been included in the test utility built into historical implementations of the sh utility.




Assuming I'd like to compare the modification timestamp between files in a /bin/sh shell script and then take action depending on whether one file is newer than the other, as in



if [ "$sigfile" -nt "$timestamp" ] ||
[ "$sigfile.tmp" -nt "$timestamp" ]
then
return
fi


... what other utility could I use, apart from make (which would make the rest of the script unwieldy to say the least)? Or should I just assume that nobody is ever going to run the script on a "historical implementation of sh", or resign to writing for a specific shell like bash?







share|improve this question





















  • As you can see in my answer, bash does not implement the -nt feature of test in a way that is expected since approx. 1995. You should use the find basd expression even with bash if you like correct behavior.
    – schily
    Jun 14 at 8:39















up vote
10
down vote

favorite












The built-in test and [ utilities have the -nt ("newer than") and -ot ("older than") tests in most shells, even when the shell is running in "POSIX mode" (also true for the external utilities of the same names on the systems that I have access to). These tests are for comparing modification timestamps on two files. Their documented semantics are slightly varying across implementations (with regards to what happens if one or the other file exists on not), but they are not included in the POSIX spec. for the test utility.




They were not carried forward into the test utility when the conditional command was removed from the [KornShell] shell because they have not been included in the test utility built into historical implementations of the sh utility.




Assuming I'd like to compare the modification timestamp between files in a /bin/sh shell script and then take action depending on whether one file is newer than the other, as in



if [ "$sigfile" -nt "$timestamp" ] ||
[ "$sigfile.tmp" -nt "$timestamp" ]
then
return
fi


... what other utility could I use, apart from make (which would make the rest of the script unwieldy to say the least)? Or should I just assume that nobody is ever going to run the script on a "historical implementation of sh", or resign to writing for a specific shell like bash?







share|improve this question





















  • As you can see in my answer, bash does not implement the -nt feature of test in a way that is expected since approx. 1995. You should use the find basd expression even with bash if you like correct behavior.
    – schily
    Jun 14 at 8:39













up vote
10
down vote

favorite









up vote
10
down vote

favorite











The built-in test and [ utilities have the -nt ("newer than") and -ot ("older than") tests in most shells, even when the shell is running in "POSIX mode" (also true for the external utilities of the same names on the systems that I have access to). These tests are for comparing modification timestamps on two files. Their documented semantics are slightly varying across implementations (with regards to what happens if one or the other file exists on not), but they are not included in the POSIX spec. for the test utility.




They were not carried forward into the test utility when the conditional command was removed from the [KornShell] shell because they have not been included in the test utility built into historical implementations of the sh utility.




Assuming I'd like to compare the modification timestamp between files in a /bin/sh shell script and then take action depending on whether one file is newer than the other, as in



if [ "$sigfile" -nt "$timestamp" ] ||
[ "$sigfile.tmp" -nt "$timestamp" ]
then
return
fi


... what other utility could I use, apart from make (which would make the rest of the script unwieldy to say the least)? Or should I just assume that nobody is ever going to run the script on a "historical implementation of sh", or resign to writing for a specific shell like bash?







share|improve this question













The built-in test and [ utilities have the -nt ("newer than") and -ot ("older than") tests in most shells, even when the shell is running in "POSIX mode" (also true for the external utilities of the same names on the systems that I have access to). These tests are for comparing modification timestamps on two files. Their documented semantics are slightly varying across implementations (with regards to what happens if one or the other file exists on not), but they are not included in the POSIX spec. for the test utility.




They were not carried forward into the test utility when the conditional command was removed from the [KornShell] shell because they have not been included in the test utility built into historical implementations of the sh utility.




Assuming I'd like to compare the modification timestamp between files in a /bin/sh shell script and then take action depending on whether one file is newer than the other, as in



if [ "$sigfile" -nt "$timestamp" ] ||
[ "$sigfile.tmp" -nt "$timestamp" ]
then
return
fi


... what other utility could I use, apart from make (which would make the rest of the script unwieldy to say the least)? Or should I just assume that nobody is ever going to run the script on a "historical implementation of sh", or resign to writing for a specific shell like bash?









share|improve this question












share|improve this question




share|improve this question








edited Jun 14 at 8:41
























asked Jun 14 at 6:57









Kusalananda

101k13199312




101k13199312











  • As you can see in my answer, bash does not implement the -nt feature of test in a way that is expected since approx. 1995. You should use the find basd expression even with bash if you like correct behavior.
    – schily
    Jun 14 at 8:39

















  • As you can see in my answer, bash does not implement the -nt feature of test in a way that is expected since approx. 1995. You should use the find basd expression even with bash if you like correct behavior.
    – schily
    Jun 14 at 8:39
















As you can see in my answer, bash does not implement the -nt feature of test in a way that is expected since approx. 1995. You should use the find basd expression even with bash if you like correct behavior.
– schily
Jun 14 at 8:39





As you can see in my answer, bash does not implement the -nt feature of test in a way that is expected since approx. 1995. You should use the find basd expression even with bash if you like correct behavior.
– schily
Jun 14 at 8:39











3 Answers
3






active

oldest

votes

















up vote
8
down vote



accepted










POSIXLY:



f1=/path/to/file_1
f2=/path/to/file_2

if [ -n "$(find -L "$f1" -prune -newer "$f2")" ]; then
printf '%s is newer than %sn' "$f1" "$f2"
fi


Using absolute path to files prevent a false positive with filename contains newlines only.



In case of using relative path, then change find command to:



find -L "$f1" -prune -newer "$f2" -exec echo . ;





share|improve this answer























  • technically, I think it fails if $f1 only contains newlines ;)
    – ilkkachu
    Jun 14 at 7:28






  • 1




    @ilkkachu could be worked around with -exec echo x ; or similar?
    – muru
    Jun 14 at 7:33










  • @muru, yeah. -printf would be easy if it were standard
    – ilkkachu
    Jun 14 at 7:36






  • 3




    @Kusalananda AFAICT, find's exit status is independent of what the individual tests of find return - except perhaps if there's an error in actually running those tests. find exits 0 even if no files are found.
    – muru
    Jun 14 at 7:46







  • 1




    Note that the behaviour of [ / -nt /nofile ] varies with the implementation (but never outputs any error).
    – Stéphane Chazelas
    Jun 14 at 9:04

















up vote
7
down vote













This could be a case for using one of the oldest Unix command, ls.



x=$(ls -tdL -- "$a" "$b")
[ "$x" = "$a
$b" ]


The result is true if a is newer than b.






share|improve this answer



















  • 3




    That doesn't work if file names start with - (missing --). You'd need -L for it to be equivalent to -nt. That doesn't work to compare x and $'xnx' for instance.
    – Stéphane Chazelas
    Jun 14 at 8:47










  • I added those, thanks.
    – meuh
    Jun 14 at 8:55






  • 1




    Eek! But also huh.
    – Kusalananda
    Jun 14 at 10:23

















up vote
4
down vote













You raised an interesting question and made a claim that should first be verified.



I checked the behavior of:



$shell -c '[ Makefile -nt SCCS/s.Makefile ] && echo newer'


with various shells. Here are the results:



  • bash Does not work - prints nothing.


  • bosh works


  • dash Does not work - prints nothing.


  • ksh88 Does not work - prints nothing.


  • ksh93 works


  • mksh Does not work - prints nothing.


  • posh prints: posh: [: -nt: unexpected operator/operand


  • yash works


  • zsh works in newer versions, older versions print nothing


So four of nine shells support the -nt feature and implement it correctly. Correctly in this case means: is able to compare time stamps on recent platforms that support sub-second time stamp granularity. Note that the files I selected differ typically only a few microseconds in their time stamps.



Since it is easier to find a working find implementation, I recommend to replace



if [ "$file1" -nt "$file2" ] ; then
echo newer
fi


by a find based expression.



if [ "$( find "$file1" -newer "$file2" )" ]; then
echo newer
fi


works at least as long as $file1 does not only contain newlines.



if [ "$( find -L "$file1" -newer "$file2" -exec echo newer ; )" ]; then
echo newer
fi


is a bit slower but works correctly.



BTW: Regarding make I cannot speak for all make implementations, but SunPro Make supports time comparison with nanosecond granularity since approx. 20 years, while smake and gmake added this feature recently.






share|improve this answer



















  • 1




    Are the "not working" tests not working because of a failure to compare sub-second timestamps (definitely?), or something else? What's the actual timestamps on the files involved in your test? +1 for the testing!
    – Kusalananda
    Jun 14 at 8:38







  • 2




    I think the downvote is probably about the confrontational wording. Here, it would be fairer to call it a limitation (significant in some contexts). Some find implementations like busybox or heirloom-toolchest will have the same limitation.
    – Stéphane Chazelas
    Jun 14 at 8:45






  • 3




    You'd need -L for the the find version to be equivalent to -nt. It would also fail on file names that start with - or !, (...
    – Stéphane Chazelas
    Jun 14 at 8:50






  • 2




    As a matter of fact, I do often get downvotes when I use a confrontational wording. Here it would help if you stated the context (files modified within same timestamp when truncated to second resolution) at the start of the answer.
    – Stéphane Chazelas
    Jun 14 at 8:52







  • 1




    @schily, yes, BSDs find have find -f "$file" for that but it's not portable.
    – Stéphane Chazelas
    Jun 14 at 8:58










Your Answer







StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "106"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f449741%2fperforming-nt-ot-test-in-a-posix-sh%23new-answer', 'question_page');

);

Post as a guest






























3 Answers
3






active

oldest

votes








3 Answers
3






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
8
down vote



accepted










POSIXLY:



f1=/path/to/file_1
f2=/path/to/file_2

if [ -n "$(find -L "$f1" -prune -newer "$f2")" ]; then
printf '%s is newer than %sn' "$f1" "$f2"
fi


Using absolute path to files prevent a false positive with filename contains newlines only.



In case of using relative path, then change find command to:



find -L "$f1" -prune -newer "$f2" -exec echo . ;





share|improve this answer























  • technically, I think it fails if $f1 only contains newlines ;)
    – ilkkachu
    Jun 14 at 7:28






  • 1




    @ilkkachu could be worked around with -exec echo x ; or similar?
    – muru
    Jun 14 at 7:33










  • @muru, yeah. -printf would be easy if it were standard
    – ilkkachu
    Jun 14 at 7:36






  • 3




    @Kusalananda AFAICT, find's exit status is independent of what the individual tests of find return - except perhaps if there's an error in actually running those tests. find exits 0 even if no files are found.
    – muru
    Jun 14 at 7:46







  • 1




    Note that the behaviour of [ / -nt /nofile ] varies with the implementation (but never outputs any error).
    – Stéphane Chazelas
    Jun 14 at 9:04














up vote
8
down vote



accepted










POSIXLY:



f1=/path/to/file_1
f2=/path/to/file_2

if [ -n "$(find -L "$f1" -prune -newer "$f2")" ]; then
printf '%s is newer than %sn' "$f1" "$f2"
fi


Using absolute path to files prevent a false positive with filename contains newlines only.



In case of using relative path, then change find command to:



find -L "$f1" -prune -newer "$f2" -exec echo . ;





share|improve this answer























  • technically, I think it fails if $f1 only contains newlines ;)
    – ilkkachu
    Jun 14 at 7:28






  • 1




    @ilkkachu could be worked around with -exec echo x ; or similar?
    – muru
    Jun 14 at 7:33










  • @muru, yeah. -printf would be easy if it were standard
    – ilkkachu
    Jun 14 at 7:36






  • 3




    @Kusalananda AFAICT, find's exit status is independent of what the individual tests of find return - except perhaps if there's an error in actually running those tests. find exits 0 even if no files are found.
    – muru
    Jun 14 at 7:46







  • 1




    Note that the behaviour of [ / -nt /nofile ] varies with the implementation (but never outputs any error).
    – Stéphane Chazelas
    Jun 14 at 9:04












up vote
8
down vote



accepted







up vote
8
down vote



accepted






POSIXLY:



f1=/path/to/file_1
f2=/path/to/file_2

if [ -n "$(find -L "$f1" -prune -newer "$f2")" ]; then
printf '%s is newer than %sn' "$f1" "$f2"
fi


Using absolute path to files prevent a false positive with filename contains newlines only.



In case of using relative path, then change find command to:



find -L "$f1" -prune -newer "$f2" -exec echo . ;





share|improve this answer















POSIXLY:



f1=/path/to/file_1
f2=/path/to/file_2

if [ -n "$(find -L "$f1" -prune -newer "$f2")" ]; then
printf '%s is newer than %sn' "$f1" "$f2"
fi


Using absolute path to files prevent a false positive with filename contains newlines only.



In case of using relative path, then change find command to:



find -L "$f1" -prune -newer "$f2" -exec echo . ;






share|improve this answer















share|improve this answer



share|improve this answer








edited Jun 15 at 3:38


























answered Jun 14 at 7:20









cuonglm

96.8k21183276




96.8k21183276











  • technically, I think it fails if $f1 only contains newlines ;)
    – ilkkachu
    Jun 14 at 7:28






  • 1




    @ilkkachu could be worked around with -exec echo x ; or similar?
    – muru
    Jun 14 at 7:33










  • @muru, yeah. -printf would be easy if it were standard
    – ilkkachu
    Jun 14 at 7:36






  • 3




    @Kusalananda AFAICT, find's exit status is independent of what the individual tests of find return - except perhaps if there's an error in actually running those tests. find exits 0 even if no files are found.
    – muru
    Jun 14 at 7:46







  • 1




    Note that the behaviour of [ / -nt /nofile ] varies with the implementation (but never outputs any error).
    – Stéphane Chazelas
    Jun 14 at 9:04
















  • technically, I think it fails if $f1 only contains newlines ;)
    – ilkkachu
    Jun 14 at 7:28






  • 1




    @ilkkachu could be worked around with -exec echo x ; or similar?
    – muru
    Jun 14 at 7:33










  • @muru, yeah. -printf would be easy if it were standard
    – ilkkachu
    Jun 14 at 7:36






  • 3




    @Kusalananda AFAICT, find's exit status is independent of what the individual tests of find return - except perhaps if there's an error in actually running those tests. find exits 0 even if no files are found.
    – muru
    Jun 14 at 7:46







  • 1




    Note that the behaviour of [ / -nt /nofile ] varies with the implementation (but never outputs any error).
    – Stéphane Chazelas
    Jun 14 at 9:04















technically, I think it fails if $f1 only contains newlines ;)
– ilkkachu
Jun 14 at 7:28




technically, I think it fails if $f1 only contains newlines ;)
– ilkkachu
Jun 14 at 7:28




1




1




@ilkkachu could be worked around with -exec echo x ; or similar?
– muru
Jun 14 at 7:33




@ilkkachu could be worked around with -exec echo x ; or similar?
– muru
Jun 14 at 7:33












@muru, yeah. -printf would be easy if it were standard
– ilkkachu
Jun 14 at 7:36




@muru, yeah. -printf would be easy if it were standard
– ilkkachu
Jun 14 at 7:36




3




3




@Kusalananda AFAICT, find's exit status is independent of what the individual tests of find return - except perhaps if there's an error in actually running those tests. find exits 0 even if no files are found.
– muru
Jun 14 at 7:46





@Kusalananda AFAICT, find's exit status is independent of what the individual tests of find return - except perhaps if there's an error in actually running those tests. find exits 0 even if no files are found.
– muru
Jun 14 at 7:46





1




1




Note that the behaviour of [ / -nt /nofile ] varies with the implementation (but never outputs any error).
– Stéphane Chazelas
Jun 14 at 9:04




Note that the behaviour of [ / -nt /nofile ] varies with the implementation (but never outputs any error).
– Stéphane Chazelas
Jun 14 at 9:04












up vote
7
down vote













This could be a case for using one of the oldest Unix command, ls.



x=$(ls -tdL -- "$a" "$b")
[ "$x" = "$a
$b" ]


The result is true if a is newer than b.






share|improve this answer



















  • 3




    That doesn't work if file names start with - (missing --). You'd need -L for it to be equivalent to -nt. That doesn't work to compare x and $'xnx' for instance.
    – Stéphane Chazelas
    Jun 14 at 8:47










  • I added those, thanks.
    – meuh
    Jun 14 at 8:55






  • 1




    Eek! But also huh.
    – Kusalananda
    Jun 14 at 10:23














up vote
7
down vote













This could be a case for using one of the oldest Unix command, ls.



x=$(ls -tdL -- "$a" "$b")
[ "$x" = "$a
$b" ]


The result is true if a is newer than b.






share|improve this answer



















  • 3




    That doesn't work if file names start with - (missing --). You'd need -L for it to be equivalent to -nt. That doesn't work to compare x and $'xnx' for instance.
    – Stéphane Chazelas
    Jun 14 at 8:47










  • I added those, thanks.
    – meuh
    Jun 14 at 8:55






  • 1




    Eek! But also huh.
    – Kusalananda
    Jun 14 at 10:23












up vote
7
down vote










up vote
7
down vote









This could be a case for using one of the oldest Unix command, ls.



x=$(ls -tdL -- "$a" "$b")
[ "$x" = "$a
$b" ]


The result is true if a is newer than b.






share|improve this answer















This could be a case for using one of the oldest Unix command, ls.



x=$(ls -tdL -- "$a" "$b")
[ "$x" = "$a
$b" ]


The result is true if a is newer than b.







share|improve this answer















share|improve this answer



share|improve this answer








edited Jun 14 at 8:53


























answered Jun 14 at 8:44









meuh

29.2k11648




29.2k11648







  • 3




    That doesn't work if file names start with - (missing --). You'd need -L for it to be equivalent to -nt. That doesn't work to compare x and $'xnx' for instance.
    – Stéphane Chazelas
    Jun 14 at 8:47










  • I added those, thanks.
    – meuh
    Jun 14 at 8:55






  • 1




    Eek! But also huh.
    – Kusalananda
    Jun 14 at 10:23












  • 3




    That doesn't work if file names start with - (missing --). You'd need -L for it to be equivalent to -nt. That doesn't work to compare x and $'xnx' for instance.
    – Stéphane Chazelas
    Jun 14 at 8:47










  • I added those, thanks.
    – meuh
    Jun 14 at 8:55






  • 1




    Eek! But also huh.
    – Kusalananda
    Jun 14 at 10:23







3




3




That doesn't work if file names start with - (missing --). You'd need -L for it to be equivalent to -nt. That doesn't work to compare x and $'xnx' for instance.
– Stéphane Chazelas
Jun 14 at 8:47




That doesn't work if file names start with - (missing --). You'd need -L for it to be equivalent to -nt. That doesn't work to compare x and $'xnx' for instance.
– Stéphane Chazelas
Jun 14 at 8:47












I added those, thanks.
– meuh
Jun 14 at 8:55




I added those, thanks.
– meuh
Jun 14 at 8:55




1




1




Eek! But also huh.
– Kusalananda
Jun 14 at 10:23




Eek! But also huh.
– Kusalananda
Jun 14 at 10:23










up vote
4
down vote













You raised an interesting question and made a claim that should first be verified.



I checked the behavior of:



$shell -c '[ Makefile -nt SCCS/s.Makefile ] && echo newer'


with various shells. Here are the results:



  • bash Does not work - prints nothing.


  • bosh works


  • dash Does not work - prints nothing.


  • ksh88 Does not work - prints nothing.


  • ksh93 works


  • mksh Does not work - prints nothing.


  • posh prints: posh: [: -nt: unexpected operator/operand


  • yash works


  • zsh works in newer versions, older versions print nothing


So four of nine shells support the -nt feature and implement it correctly. Correctly in this case means: is able to compare time stamps on recent platforms that support sub-second time stamp granularity. Note that the files I selected differ typically only a few microseconds in their time stamps.



Since it is easier to find a working find implementation, I recommend to replace



if [ "$file1" -nt "$file2" ] ; then
echo newer
fi


by a find based expression.



if [ "$( find "$file1" -newer "$file2" )" ]; then
echo newer
fi


works at least as long as $file1 does not only contain newlines.



if [ "$( find -L "$file1" -newer "$file2" -exec echo newer ; )" ]; then
echo newer
fi


is a bit slower but works correctly.



BTW: Regarding make I cannot speak for all make implementations, but SunPro Make supports time comparison with nanosecond granularity since approx. 20 years, while smake and gmake added this feature recently.






share|improve this answer



















  • 1




    Are the "not working" tests not working because of a failure to compare sub-second timestamps (definitely?), or something else? What's the actual timestamps on the files involved in your test? +1 for the testing!
    – Kusalananda
    Jun 14 at 8:38







  • 2




    I think the downvote is probably about the confrontational wording. Here, it would be fairer to call it a limitation (significant in some contexts). Some find implementations like busybox or heirloom-toolchest will have the same limitation.
    – Stéphane Chazelas
    Jun 14 at 8:45






  • 3




    You'd need -L for the the find version to be equivalent to -nt. It would also fail on file names that start with - or !, (...
    – Stéphane Chazelas
    Jun 14 at 8:50






  • 2




    As a matter of fact, I do often get downvotes when I use a confrontational wording. Here it would help if you stated the context (files modified within same timestamp when truncated to second resolution) at the start of the answer.
    – Stéphane Chazelas
    Jun 14 at 8:52







  • 1




    @schily, yes, BSDs find have find -f "$file" for that but it's not portable.
    – Stéphane Chazelas
    Jun 14 at 8:58














up vote
4
down vote













You raised an interesting question and made a claim that should first be verified.



I checked the behavior of:



$shell -c '[ Makefile -nt SCCS/s.Makefile ] && echo newer'


with various shells. Here are the results:



  • bash Does not work - prints nothing.


  • bosh works


  • dash Does not work - prints nothing.


  • ksh88 Does not work - prints nothing.


  • ksh93 works


  • mksh Does not work - prints nothing.


  • posh prints: posh: [: -nt: unexpected operator/operand


  • yash works


  • zsh works in newer versions, older versions print nothing


So four of nine shells support the -nt feature and implement it correctly. Correctly in this case means: is able to compare time stamps on recent platforms that support sub-second time stamp granularity. Note that the files I selected differ typically only a few microseconds in their time stamps.



Since it is easier to find a working find implementation, I recommend to replace



if [ "$file1" -nt "$file2" ] ; then
echo newer
fi


by a find based expression.



if [ "$( find "$file1" -newer "$file2" )" ]; then
echo newer
fi


works at least as long as $file1 does not only contain newlines.



if [ "$( find -L "$file1" -newer "$file2" -exec echo newer ; )" ]; then
echo newer
fi


is a bit slower but works correctly.



BTW: Regarding make I cannot speak for all make implementations, but SunPro Make supports time comparison with nanosecond granularity since approx. 20 years, while smake and gmake added this feature recently.






share|improve this answer



















  • 1




    Are the "not working" tests not working because of a failure to compare sub-second timestamps (definitely?), or something else? What's the actual timestamps on the files involved in your test? +1 for the testing!
    – Kusalananda
    Jun 14 at 8:38







  • 2




    I think the downvote is probably about the confrontational wording. Here, it would be fairer to call it a limitation (significant in some contexts). Some find implementations like busybox or heirloom-toolchest will have the same limitation.
    – Stéphane Chazelas
    Jun 14 at 8:45






  • 3




    You'd need -L for the the find version to be equivalent to -nt. It would also fail on file names that start with - or !, (...
    – Stéphane Chazelas
    Jun 14 at 8:50






  • 2




    As a matter of fact, I do often get downvotes when I use a confrontational wording. Here it would help if you stated the context (files modified within same timestamp when truncated to second resolution) at the start of the answer.
    – Stéphane Chazelas
    Jun 14 at 8:52







  • 1




    @schily, yes, BSDs find have find -f "$file" for that but it's not portable.
    – Stéphane Chazelas
    Jun 14 at 8:58












up vote
4
down vote










up vote
4
down vote









You raised an interesting question and made a claim that should first be verified.



I checked the behavior of:



$shell -c '[ Makefile -nt SCCS/s.Makefile ] && echo newer'


with various shells. Here are the results:



  • bash Does not work - prints nothing.


  • bosh works


  • dash Does not work - prints nothing.


  • ksh88 Does not work - prints nothing.


  • ksh93 works


  • mksh Does not work - prints nothing.


  • posh prints: posh: [: -nt: unexpected operator/operand


  • yash works


  • zsh works in newer versions, older versions print nothing


So four of nine shells support the -nt feature and implement it correctly. Correctly in this case means: is able to compare time stamps on recent platforms that support sub-second time stamp granularity. Note that the files I selected differ typically only a few microseconds in their time stamps.



Since it is easier to find a working find implementation, I recommend to replace



if [ "$file1" -nt "$file2" ] ; then
echo newer
fi


by a find based expression.



if [ "$( find "$file1" -newer "$file2" )" ]; then
echo newer
fi


works at least as long as $file1 does not only contain newlines.



if [ "$( find -L "$file1" -newer "$file2" -exec echo newer ; )" ]; then
echo newer
fi


is a bit slower but works correctly.



BTW: Regarding make I cannot speak for all make implementations, but SunPro Make supports time comparison with nanosecond granularity since approx. 20 years, while smake and gmake added this feature recently.






share|improve this answer















You raised an interesting question and made a claim that should first be verified.



I checked the behavior of:



$shell -c '[ Makefile -nt SCCS/s.Makefile ] && echo newer'


with various shells. Here are the results:



  • bash Does not work - prints nothing.


  • bosh works


  • dash Does not work - prints nothing.


  • ksh88 Does not work - prints nothing.


  • ksh93 works


  • mksh Does not work - prints nothing.


  • posh prints: posh: [: -nt: unexpected operator/operand


  • yash works


  • zsh works in newer versions, older versions print nothing


So four of nine shells support the -nt feature and implement it correctly. Correctly in this case means: is able to compare time stamps on recent platforms that support sub-second time stamp granularity. Note that the files I selected differ typically only a few microseconds in their time stamps.



Since it is easier to find a working find implementation, I recommend to replace



if [ "$file1" -nt "$file2" ] ; then
echo newer
fi


by a find based expression.



if [ "$( find "$file1" -newer "$file2" )" ]; then
echo newer
fi


works at least as long as $file1 does not only contain newlines.



if [ "$( find -L "$file1" -newer "$file2" -exec echo newer ; )" ]; then
echo newer
fi


is a bit slower but works correctly.



BTW: Regarding make I cannot speak for all make implementations, but SunPro Make supports time comparison with nanosecond granularity since approx. 20 years, while smake and gmake added this feature recently.







share|improve this answer















share|improve this answer



share|improve this answer








edited Jun 14 at 9:16


























answered Jun 14 at 8:24









schily

8,57421435




8,57421435







  • 1




    Are the "not working" tests not working because of a failure to compare sub-second timestamps (definitely?), or something else? What's the actual timestamps on the files involved in your test? +1 for the testing!
    – Kusalananda
    Jun 14 at 8:38







  • 2




    I think the downvote is probably about the confrontational wording. Here, it would be fairer to call it a limitation (significant in some contexts). Some find implementations like busybox or heirloom-toolchest will have the same limitation.
    – Stéphane Chazelas
    Jun 14 at 8:45






  • 3




    You'd need -L for the the find version to be equivalent to -nt. It would also fail on file names that start with - or !, (...
    – Stéphane Chazelas
    Jun 14 at 8:50






  • 2




    As a matter of fact, I do often get downvotes when I use a confrontational wording. Here it would help if you stated the context (files modified within same timestamp when truncated to second resolution) at the start of the answer.
    – Stéphane Chazelas
    Jun 14 at 8:52







  • 1




    @schily, yes, BSDs find have find -f "$file" for that but it's not portable.
    – Stéphane Chazelas
    Jun 14 at 8:58












  • 1




    Are the "not working" tests not working because of a failure to compare sub-second timestamps (definitely?), or something else? What's the actual timestamps on the files involved in your test? +1 for the testing!
    – Kusalananda
    Jun 14 at 8:38







  • 2




    I think the downvote is probably about the confrontational wording. Here, it would be fairer to call it a limitation (significant in some contexts). Some find implementations like busybox or heirloom-toolchest will have the same limitation.
    – Stéphane Chazelas
    Jun 14 at 8:45






  • 3




    You'd need -L for the the find version to be equivalent to -nt. It would also fail on file names that start with - or !, (...
    – Stéphane Chazelas
    Jun 14 at 8:50






  • 2




    As a matter of fact, I do often get downvotes when I use a confrontational wording. Here it would help if you stated the context (files modified within same timestamp when truncated to second resolution) at the start of the answer.
    – Stéphane Chazelas
    Jun 14 at 8:52







  • 1




    @schily, yes, BSDs find have find -f "$file" for that but it's not portable.
    – Stéphane Chazelas
    Jun 14 at 8:58







1




1




Are the "not working" tests not working because of a failure to compare sub-second timestamps (definitely?), or something else? What's the actual timestamps on the files involved in your test? +1 for the testing!
– Kusalananda
Jun 14 at 8:38





Are the "not working" tests not working because of a failure to compare sub-second timestamps (definitely?), or something else? What's the actual timestamps on the files involved in your test? +1 for the testing!
– Kusalananda
Jun 14 at 8:38





2




2




I think the downvote is probably about the confrontational wording. Here, it would be fairer to call it a limitation (significant in some contexts). Some find implementations like busybox or heirloom-toolchest will have the same limitation.
– Stéphane Chazelas
Jun 14 at 8:45




I think the downvote is probably about the confrontational wording. Here, it would be fairer to call it a limitation (significant in some contexts). Some find implementations like busybox or heirloom-toolchest will have the same limitation.
– Stéphane Chazelas
Jun 14 at 8:45




3




3




You'd need -L for the the find version to be equivalent to -nt. It would also fail on file names that start with - or !, (...
– Stéphane Chazelas
Jun 14 at 8:50




You'd need -L for the the find version to be equivalent to -nt. It would also fail on file names that start with - or !, (...
– Stéphane Chazelas
Jun 14 at 8:50




2




2




As a matter of fact, I do often get downvotes when I use a confrontational wording. Here it would help if you stated the context (files modified within same timestamp when truncated to second resolution) at the start of the answer.
– Stéphane Chazelas
Jun 14 at 8:52





As a matter of fact, I do often get downvotes when I use a confrontational wording. Here it would help if you stated the context (files modified within same timestamp when truncated to second resolution) at the start of the answer.
– Stéphane Chazelas
Jun 14 at 8:52





1




1




@schily, yes, BSDs find have find -f "$file" for that but it's not portable.
– Stéphane Chazelas
Jun 14 at 8:58




@schily, yes, BSDs find have find -f "$file" for that but it's not portable.
– Stéphane Chazelas
Jun 14 at 8:58












 

draft saved


draft discarded


























 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f449741%2fperforming-nt-ot-test-in-a-posix-sh%23new-answer', 'question_page');

);

Post as a guest













































































Popular posts from this blog

How to check contact read email or not when send email to Individual?

Bahrain

Postfix configuration issue with fips on centos 7; mailgun relay