Make a backup file: Difference between revisions
Applesoft BASIC |
Thundergnat (talk | contribs) m syntax highlighting fixup automation |
||
Line 17: | Line 17: | ||
{{trans|Python}} |
{{trans|Python}} |
||
< |
<syntaxhighlight lang="11l">V targetfile = ‘pycon-china’ |
||
fs:rename(fs:path:canonical(targetfile), fs:path:canonical(targetfile)‘.bak’) |
fs:rename(fs:path:canonical(targetfile), fs:path:canonical(targetfile)‘.bak’) |
||
V f = File(fs:path:canonical(targetfile), ‘w’) |
V f = File(fs:path:canonical(targetfile), ‘w’) |
||
f.write(‘this task was solved during a talk about rosettacode at the PyCon China in 2011’) |
f.write(‘this task was solved during a talk about rosettacode at the PyCon China in 2011’) |
||
f.close()</ |
f.close()</syntaxhighlight> |
||
=={{header|Applesoft BASIC}}== |
=={{header|Applesoft BASIC}}== |
||
Due to all the pitfalls in this task it is helpful to turn on debugging by default. It is also helpful, to show each DOS 3.3 Command, by entering MON C before running the program. There is an extra PRINT statement in line 540 to work-around an issue with the display of DOS 3.3 commands after an error occurs. Setting ERASEANYBACKUP will delete the backup file if it already exists. Delete lines 180 to 230 with DEL 180,230 and it is possible with DOS 3.3 to have backup files all with the same name due to a pitfall in the RENAME Command. |
Due to all the pitfalls in this task it is helpful to turn on debugging by default. It is also helpful, to show each DOS 3.3 Command, by entering MON C before running the program. There is an extra PRINT statement in line 540 to work-around an issue with the display of DOS 3.3 commands after an error occurs. Setting ERASEANYBACKUP will delete the backup file if it already exists. Delete lines 180 to 230 with DEL 180,230 and it is possible with DOS 3.3 to have backup files all with the same name due to a pitfall in the RENAME Command. |
||
< |
<syntaxhighlight lang="gwbasic"> 100 ERASEANYBACKUP = FALSE |
||
110 DEBUG = NOT FALSE |
110 DEBUG = NOT FALSE |
||
120 F$ = "FILE" |
120 F$ = "FILE" |
||
Line 62: | Line 62: | ||
540 PRINT |
540 PRINT |
||
550 IF DEBUG THEN PRINT " <<< "N$" DOES NOT EXIST." |
550 IF DEBUG THEN PRINT " <<< "N$" DOES NOT EXIST." |
||
560 RETURN</ |
560 RETURN</syntaxhighlight> |
||
<lang>DELETE FILE |
<syntaxhighlight lang="text">DELETE FILE |
||
DELETE BACKUP FILE |
DELETE BACKUP FILE |
||
MON C</ |
MON C</syntaxhighlight> |
||
<lang |
<syntaxhighlight lang="gwbasic">RUN</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 76: | Line 76: | ||
CLOSE FILE |
CLOSE FILE |
||
</pre> |
</pre> |
||
<lang |
<syntaxhighlight lang="gwbasic">RUN</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 90: | Line 90: | ||
CLOSE FILE |
CLOSE FILE |
||
</pre> |
</pre> |
||
<lang |
<syntaxhighlight lang="gwbasic">RUN</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 101: | Line 101: | ||
</pre> |
</pre> |
||
=={{header|AutoHotkey}}== |
=={{header|AutoHotkey}}== |
||
< |
<syntaxhighlight lang="autohotkey">targetfile := "ahk-file" |
||
if FileExist(targetfile) |
if FileExist(targetfile) |
||
FileMove, %targetfile%, %targetfile%.bak |
FileMove, %targetfile%, %targetfile%.bak |
||
Line 113: | Line 113: | ||
} |
} |
||
file.Write("This is a test string.`r`n") |
file.Write("This is a test string.`r`n") |
||
file.Close()</ |
file.Close()</syntaxhighlight> |
||
=={{header|AWK}}== |
=={{header|AWK}}== |
||
<syntaxhighlight lang="awk"> |
|||
<lang AWK> |
|||
# syntax: GAWK -f MAKE_A_BACKUP_FILE.AWK filename(s) |
# syntax: GAWK -f MAKE_A_BACKUP_FILE.AWK filename(s) |
||
# see: http://www.gnu.org/software/gawk/manual/gawk.html#Extension-Sample-Inplace |
# see: http://www.gnu.org/software/gawk/manual/gawk.html#Extension-Sample-Inplace |
||
Line 133: | Line 133: | ||
exit(0) |
exit(0) |
||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Batch File}}== |
=={{header|Batch File}}== |
||
< |
<syntaxhighlight lang="dos"> |
||
@echo off |
@echo off |
||
setlocal enabledelayedexpansion |
setlocal enabledelayedexpansion |
||
Line 157: | Line 157: | ||
echo !line[1]!>"%filePath%" |
echo !line[1]!>"%filePath%" |
||
for /l %%i in (2,1,%i%) do echo !line[%%i]!>>"%filePath%" |
for /l %%i in (2,1,%i%) do echo !line[%%i]!>>"%filePath%" |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Common Lisp}}== |
=={{header|Common Lisp}}== |
||
Appends a version number and increments it on each backup |
Appends a version number and increments it on each backup |
||
< |
<syntaxhighlight lang="lisp">(defun parse-integer-quietly (&rest args) |
||
(ignore-errors (apply #'parse-integer args))) |
(ignore-errors (apply #'parse-integer args))) |
||
Line 179: | Line 179: | ||
(rename-file file (get-next-version file)) |
(rename-file file (get-next-version file)) |
||
(with-open-file (out file :direction :output) |
(with-open-file (out file :direction :output) |
||
(print data out))))</ |
(print data out))))</syntaxhighlight> |
||
=={{header|Elixir}}== |
=={{header|Elixir}}== |
||
< |
<syntaxhighlight lang="elixir">defmodule RC do |
||
def backup_file(filename) do |
def backup_file(filename) do |
||
backup = filename <> ".backup" |
backup = filename <> ".backup" |
||
Line 193: | Line 193: | ||
end |
end |
||
hd(System.argv) |> RC.backup_file</ |
hd(System.argv) |> RC.backup_file</syntaxhighlight> |
||
=={{header|Go}}== |
=={{header|Go}}== |
||
===Rename=== |
===Rename=== |
||
This is the technique of the task description. |
This is the technique of the task description. |
||
< |
<syntaxhighlight lang="go">package main |
||
import ( |
import ( |
||
Line 230: | Line 230: | ||
fmt.Println(err) |
fmt.Println(err) |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
===Copy=== |
===Copy=== |
||
Alternative technique copies an existing file to make the backup copy, then updates the origial file. In an attempt to keep operations atomic, the original file is opened as the first operation and is not closed until all operations are complete. In an attempt to avoid data loss, the original file is not modified until the backup file is closed. |
Alternative technique copies an existing file to make the backup copy, then updates the origial file. In an attempt to keep operations atomic, the original file is opened as the first operation and is not closed until all operations are complete. In an attempt to avoid data loss, the original file is not modified until the backup file is closed. |
||
< |
<syntaxhighlight lang="go">package main |
||
import ( |
import ( |
||
Line 313: | Line 313: | ||
// backup complete (as long as err == nil) |
// backup complete (as long as err == nil) |
||
return |
return |
||
}</ |
}</syntaxhighlight> |
||
=={{header|Java}}== |
=={{header|Java}}== |
||
{{works with|Java|7+}} |
{{works with|Java|7+}} |
||
< |
<syntaxhighlight lang="java5">import java.io.File; |
||
import java.io.IOException; |
import java.io.IOException; |
||
import java.io.PrintWriter; |
import java.io.PrintWriter; |
||
Line 351: | Line 351: | ||
} |
} |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
Contents of 'original.txt' ''before'' the program is run and of 'original.txt.backup' ''after'' it is run: |
Contents of 'original.txt' ''before'' the program is run and of 'original.txt.backup' ''after'' it is run: |
||
Line 368: | Line 368: | ||
{{works with|Java|1.5+}} |
{{works with|Java|1.5+}} |
||
< |
<syntaxhighlight lang="java5">import java.io.File; |
||
import java.io.IOException; |
import java.io.IOException; |
||
import java.io.PrintWriter; |
import java.io.PrintWriter; |
||
Line 397: | Line 397: | ||
} |
} |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 405: | Line 405: | ||
{{works with|Julia|0.6}} |
{{works with|Julia|0.6}} |
||
< |
<syntaxhighlight lang="julia">targetfile = "pycon-china" |
||
mv(realpath(targetfile), realpath(targetfile) * ".bak") |
mv(realpath(targetfile), realpath(targetfile) * ".bak") |
||
# "a+" for permissions of reading, writing, creating |
# "a+" for permissions of reading, writing, creating |
||
open(targetfile, "w+") do io |
open(targetfile, "w+") do io |
||
println(io, "this task was solved during a talk about rosettacode at the PyCon China in 2011") |
println(io, "this task was solved during a talk about rosettacode at the PyCon China in 2011") |
||
end</ |
end</syntaxhighlight> |
||
=={{header|Kotlin}}== |
=={{header|Kotlin}}== |
||
{{trans|Java}} |
{{trans|Java}} |
||
< |
<syntaxhighlight lang="scala">// version 1.1.51 |
||
import java.io.File |
import java.io.File |
||
Line 433: | Line 433: | ||
fun main(args: Array<String>) { |
fun main(args: Array<String>) { |
||
saveWithBackup("original.txt", "fourth", "fifth", "sixth") |
saveWithBackup("original.txt", "fourth", "fifth", "sixth") |
||
}</ |
}</syntaxhighlight> |
||
Contents of 'original.txt' ''before'' the program is run and of 'original.txt.backup' ''after'' it is run: |
Contents of 'original.txt' ''before'' the program is run and of 'original.txt.backup' ''after'' it is run: |
||
Line 450: | Line 450: | ||
=={{header|Lasso}}== |
=={{header|Lasso}}== |
||
< |
<syntaxhighlight lang="lasso">local(file2use = 'input.txt') |
||
// create file |
// create file |
||
Line 469: | Line 469: | ||
// create new file with new contents |
// create new file with new contents |
||
local(nf = file(#file2use)) |
local(nf = file(#file2use)) |
||
#nf->doWithClose => { #nf->writeBytes(#contents_of_f->asBytes) }</ |
#nf->doWithClose => { #nf->writeBytes(#contents_of_f->asBytes) }</syntaxhighlight> |
||
=={{header|Locomotive Basic}}== |
=={{header|Locomotive Basic}}== |
||
Line 477: | Line 477: | ||
=={{header|Nim}}== |
=={{header|Nim}}== |
||
In case the backup cannot be done, an exception is raised. |
In case the backup cannot be done, an exception is raised. |
||
< |
<syntaxhighlight lang="nim">import os, strutils |
||
const |
const |
||
Line 525: | Line 525: | ||
# Cleanup. |
# Cleanup. |
||
setCurrentDir(oldDir) |
setCurrentDir(oldDir) |
||
removeDir(Dir)</ |
removeDir(Dir)</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 541: | Line 541: | ||
=={{header|Perl}}== |
=={{header|Perl}}== |
||
< |
<syntaxhighlight lang="perl">use strict; |
||
use warnings; |
use warnings; |
||
Line 580: | Line 580: | ||
# back up this program |
# back up this program |
||
backup($0,3,'.bk');</ |
backup($0,3,'.bk');</syntaxhighlight> |
||
=={{header|Phix}}== |
=={{header|Phix}}== |
||
{{trans|Go}} |
{{trans|Go}} |
||
< |
<syntaxhighlight lang="phix">targetfile = get_proper_path("test.txt") |
||
if not rename_file(targetfile, targetfile&".bak", overwrite:=true) then |
if not rename_file(targetfile, targetfile&".bak", overwrite:=true) then |
||
puts(1,"warning: could not rename file\n") |
puts(1,"warning: could not rename file\n") |
||
Line 594: | Line 594: | ||
puts(fn,"this task was translated from the Python entry\n") |
puts(fn,"this task was translated from the Python entry\n") |
||
close(fn) |
close(fn) |
||
end if</ |
end if</syntaxhighlight> |
||
Before basing anything on the above code, though, I would recommend you take a look at <br> |
Before basing anything on the above code, though, I would recommend you take a look at <br> |
||
function saveFile in demo\edix\edix.exw, which does this sort of thing for real: <br> |
function saveFile in demo\edix\edix.exw, which does this sort of thing for real: <br> |
||
Line 601: | Line 601: | ||
=={{header|PicoLisp}}== |
=={{header|PicoLisp}}== |
||
PicoLisp makes use of external commands as much as possible (at least for not time-critical operations), to avoid duplicated functionality. |
PicoLisp makes use of external commands as much as possible (at least for not time-critical operations), to avoid duplicated functionality. |
||
< |
<syntaxhighlight lang="picolisp">(let Path (in '(realpath "foo") (line T)) |
||
(call 'mv Path (pack Path ".backup")) |
(call 'mv Path (pack Path ".backup")) |
||
(out Path |
(out Path |
||
(prinl "This is the new file") ) )</ |
(prinl "This is the new file") ) )</syntaxhighlight> |
||
=={{header|Pike}}== |
=={{header|Pike}}== |
||
< |
<syntaxhighlight lang="pike">string targetfile = "pycon-china"; |
||
targetfile = System.resolvepath(targetfile); |
targetfile = System.resolvepath(targetfile); |
||
mv(targetfile, targetfile+"~"); |
mv(targetfile, targetfile+"~"); |
||
Stdio.write_file(targetfile, "this task was solved at the pycon china 2011");</ |
Stdio.write_file(targetfile, "this task was solved at the pycon china 2011");</syntaxhighlight> |
||
=={{header|Python}}== |
=={{header|Python}}== |
||
Using [https://docs.python.org/library/os.html os library] |
Using [https://docs.python.org/library/os.html os library] |
||
<syntaxhighlight lang="python"> |
|||
<lang Python> |
|||
import os |
import os |
||
targetfile = "pycon-china" |
targetfile = "pycon-china" |
||
Line 621: | Line 621: | ||
f.write("this task was solved during a talk about rosettacode at the PyCon China in 2011") |
f.write("this task was solved during a talk about rosettacode at the PyCon China in 2011") |
||
f.close() |
f.close() |
||
</syntaxhighlight> |
|||
</lang> |
|||
Or using a newer [https://docs.python.org/library/pathlib.html pathlib library] (Python >= 3.4): |
Or using a newer [https://docs.python.org/library/pathlib.html pathlib library] (Python >= 3.4): |
||
<syntaxhighlight lang="python"> |
|||
<lang Python> |
|||
from pathlib import Path |
from pathlib import Path |
||
Line 630: | Line 630: | ||
with filepath.open('w') as file: |
with filepath.open('w') as file: |
||
file.write("New content") |
file.write("New content") |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Racket}}== |
=={{header|Racket}}== |
||
Line 636: | Line 636: | ||
This version keeps unlimited backups, with <tt>*.bak</tt> being the freshest one, <tt>*.bak1</tt> is an older backup, etc. So each backup moves all existing names up. |
This version keeps unlimited backups, with <tt>*.bak</tt> being the freshest one, <tt>*.bak1</tt> is an older backup, etc. So each backup moves all existing names up. |
||
<syntaxhighlight lang="racket"> |
|||
<lang Racket> |
|||
#lang racket |
#lang racket |
||
Line 654: | Line 654: | ||
(revise "fff") |
(revise "fff") |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Raku}}== |
=={{header|Raku}}== |
||
Line 660: | Line 660: | ||
{{works with|Rakudo|2017.10}} |
{{works with|Rakudo|2017.10}} |
||
<lang |
<syntaxhighlight lang="raku" line># Back up the given path/filename with a default extension .bk(n) |
||
# where n is in the range 1 - $limit (default 3). |
# where n is in the range 1 - $limit (default 3). |
||
# Prints 'File not found' to STDERR if the file does not exist. |
# Prints 'File not found' to STDERR if the file does not exist. |
||
Line 694: | Line 694: | ||
# Optionally, specify limit, back-up extension pattern and whether to follow symlinks. |
# Optionally, specify limit, back-up extension pattern and whether to follow symlinks. |
||
# Optional parameters can be in any order, in any combination. |
# Optional parameters can be in any order, in any combination. |
||
backup 'myfile', :follow-symlinks, :limit(2), :ext('bak');</ |
backup 'myfile', :follow-symlinks, :limit(2), :ext('bak');</syntaxhighlight> |
||
=={{header|REXX}}== |
=={{header|REXX}}== |
||
This REXX version executes under DOS or DOS under Windows. |
This REXX version executes under DOS or DOS under Windows. |
||
< |
<syntaxhighlight lang="rexx">/*REXX program creates a backup file (for a given file), then overwrites the old file.*/ |
||
parse arg oFID . /*get a required argument from the C.L.*/ |
parse arg oFID . /*get a required argument from the C.L.*/ |
||
if oFID=='' then do /*No argument? Then issue an err msg.*/ |
if oFID=='' then do /*No argument? Then issue an err msg.*/ |
||
Line 710: | Line 710: | ||
'RENAME' oFID tFID /*rename the original file to backup. */ |
'RENAME' oFID tFID /*rename the original file to backup. */ |
||
call lineout oFID, '═══This is line 1.' /*write one line to the original file. */ |
call lineout oFID, '═══This is line 1.' /*write one line to the original file. */ |
||
/*stick a fork in it, we're all done. */</ |
/*stick a fork in it, we're all done. */</syntaxhighlight> |
||
The contents of the original file (before execution): '''A.FILE''': |
The contents of the original file (before execution): '''A.FILE''': |
||
<pre> |
<pre> |
||
Line 735: | Line 735: | ||
=={{header|Ruby}}== |
=={{header|Ruby}}== |
||
This version does not overwrite the backup file if it exists. |
This version does not overwrite the backup file if it exists. |
||
< |
<syntaxhighlight lang="ruby">def backup_and_open(filename) |
||
filename = File.realpath(filename) |
filename = File.realpath(filename) |
||
bkup = filename + ".backup" |
bkup = filename + ".backup" |
||
Line 753: | Line 753: | ||
end |
end |
||
1.upto(12) {|i| backup_and_open(ARGV[0]) {|fh| fh.puts "backup #{i}"}}</ |
1.upto(12) {|i| backup_and_open(ARGV[0]) {|fh| fh.puts "backup #{i}"}}</syntaxhighlight> |
||
Example: |
Example: |
||
Line 791: | Line 791: | ||
=={{header|Scala}}== |
=={{header|Scala}}== |
||
===Java Interoperability=== |
===Java Interoperability=== |
||
< |
<syntaxhighlight lang="scala">import java.io.{File, PrintWriter} |
||
import java.nio.file.{Files, Paths, StandardCopyOption} |
import java.nio.file.{Files, Paths, StandardCopyOption} |
||
Line 813: | Line 813: | ||
saveWithBackup("original.txt", "fourth", "fifth", "sixth") |
saveWithBackup("original.txt", "fourth", "fifth", "sixth") |
||
}</ |
}</syntaxhighlight> |
||
=={{header|Tcl}}== |
=={{header|Tcl}}== |
||
< |
<syntaxhighlight lang="tcl">package require Tcl 8.5 |
||
proc backupopen {filename mode} { |
proc backupopen {filename mode} { |
||
Line 837: | Line 837: | ||
} |
} |
||
return [open $filename $mode] |
return [open $filename $mode] |
||
}</ |
}</syntaxhighlight> |
||
=={{header|VBA}}== |
=={{header|VBA}}== |
||
< |
<syntaxhighlight lang="vb">Public Sub backup(filename As String) |
||
If Len(Dir(filename)) > 0 Then |
If Len(Dir(filename)) > 0 Then |
||
On Error Resume Next |
On Error Resume Next |
||
Line 857: | Line 857: | ||
Public Sub main() |
Public Sub main() |
||
backup "D:\test.txt" |
backup "D:\test.txt" |
||
End Sub</ |
End Sub</syntaxhighlight> |
||
=={{header|Wren}}== |
=={{header|Wren}}== |
||
{{libheader|Wren-ioutil}} |
{{libheader|Wren-ioutil}} |
||
< |
<syntaxhighlight lang="ecmascript">import "/ioutil" for File, FileUtil |
||
var saveWithBackup = Fn.new { |filePath, lines| |
var saveWithBackup = Fn.new { |filePath, lines| |
||
Line 879: | Line 879: | ||
System.print(File.read("original.txt")) |
System.print(File.read("original.txt")) |
||
System.print("Contents of original.txt.backup:") |
System.print("Contents of original.txt.backup:") |
||
System.print(File.read("original.txt.backup"))</ |
System.print(File.read("original.txt.backup"))</syntaxhighlight> |
||
{{out}} |
{{out}} |
Revision as of 21:53, 27 August 2022
Before writing to a file it is often advisable to make a backup of the original. Creating such a backup file is however also not without pitfalls.
In this task you should create a backup file from an existing file and then write new text to the old file. The following issues should be handled:
- avoid making a copy of the file but instead rename the original and then write a new file with the original filename.
- if a copy needs to be made, please explain why rename is not possible.
- keep in mind symlinks, and do not rename or copy the link but the target. (If there is a link
foo -> bar/baz
, thenbar/baz
should be renamed tobar/baz.backup
and then the new text should be written tobar/baz
.) - it is assumed that you have permission to write in the target location, thus permission errors need not be handled.
- you may choose the backup filename per preference or given limitations. (It should somehow include the original filename however.)
- please try to avoid executing external commands, and especially avoid calling a shell script.
Some examples on this page assume that the original file already exists. They might fail if some user is trying to create a new file.
11l
V targetfile = ‘pycon-china’
fs:rename(fs:path:canonical(targetfile), fs:path:canonical(targetfile)‘.bak’)
V f = File(fs:path:canonical(targetfile), ‘w’)
f.write(‘this task was solved during a talk about rosettacode at the PyCon China in 2011’)
f.close()
Applesoft BASIC
Due to all the pitfalls in this task it is helpful to turn on debugging by default. It is also helpful, to show each DOS 3.3 Command, by entering MON C before running the program. There is an extra PRINT statement in line 540 to work-around an issue with the display of DOS 3.3 commands after an error occurs. Setting ERASEANYBACKUP will delete the backup file if it already exists. Delete lines 180 to 230 with DEL 180,230 and it is possible with DOS 3.3 to have backup files all with the same name due to a pitfall in the RENAME Command.
100 ERASEANYBACKUP = FALSE
110 DEBUG = NOT FALSE
120 F$ = "FILE"
130 LET B$ = "BACKUP " + F$
140 LET D$ = CHR$ (4)
150 N$ = F$
160 GOSUB 400"FILE EXISTS?"
170 IF NOT E GOTO 260"MAKE NEW FILE"
180 N$ = B$
190 GOSUB 400"FILE EXISTS?"
200 IF NOT E GOTO 240"KEEP FILE AS BACKUP FILE"
210 IF DEBUG AND NOT ERASEANYBACKUP THEN PRINT " *** "B$" WON'T BE DELETED.";
220 IF NOT ERASEANYBACKUP THEN STOP
230 PRINT D$"DELETE "B$
240 IF DEBUG THEN PRINT " >>> MAKING A BACKUP FILE."
250 PRINT D$"RENAME "F$","B$
260 IF DEBUG THEN PRINT " >>> MAKING A NEW FILE."
270 PRINT D$"OPEN "F$
280 PRINT D$"WRITE "F$
290 PRINT "THE NEW CONTENT OF THE FILE."
300 PRINT D$"CLOSE "F$
310 END
400 ONERR GOTO 500"CATCH"
410 PRINT D$"VERIFY "N$
420 POKE 216,0: REM ONERR OFF
430 LET E = 1
440 IF DEBUG THEN PRINT " <<< "N$" EXISTS."
450 RETURN
500 LET E = PEEK (222) < > 6
510 POKE 216,0: REM ONERR OFF
520 IF E THEN RESUME : REM THROW
530 CALL - 3288: REM RECOVER
540 PRINT
550 IF DEBUG THEN PRINT " <<< "N$" DOES NOT EXIST."
560 RETURN
DELETE FILE
DELETE BACKUP FILE
MON C
RUN
- Output:
VERIFY FILE <<< FILE DOES NOT EXIST. >>> MAKING A NEW FILE. OPEN FILE WRITE FILE CLOSE FILE
RUN
- Output:
VERIFY FILE <<< FILE EXISTS. VERIFY BACKUP FILE <<< BACKUP FILE DOES NOT EXIST. >>> MAKING A BACKUP FILE. RENAME FILE,BACKUP FILE >>> MAKING A NEW FILE. OPEN FILE WRITE FILE CLOSE FILE
RUN
- Output:
VERIFY FILE <<< FILE EXISTS. VERIFY BACKUP FILE <<< BACKUP FILE EXISTS. *** BACKUP FILE WON'T BE DELETED. BREAK IN 220
AutoHotkey
targetfile := "ahk-file"
if FileExist(targetfile)
FileMove, %targetfile%, %targetfile%.bak
else
FileAppend,, %targetfile%
file := FileOpen(targetfile, "w")
if !IsObject(file)
{
MsgBox Can't open "%FileName%" for writing.
return
}
file.Write("This is a test string.`r`n")
file.Close()
AWK
# syntax: GAWK -f MAKE_A_BACKUP_FILE.AWK filename(s)
# see: http://www.gnu.org/software/gawk/manual/gawk.html#Extension-Sample-Inplace
@load "inplace"
BEGIN {
INPLACE_SUFFIX = ".BAK"
}
BEGINFILE {
inplace_begin(FILENAME,INPLACE_SUFFIX)
}
1 # rewrite file unchanged
ENDFILE {
inplace_end(FILENAME,INPLACE_SUFFIX)
}
END {
exit(0)
}
Batch File
@echo off
setlocal enabledelayedexpansion
:: Takes file input as param #1
set "filePath=%~1"
set "fileName=%~xn1"
:: Save file contents of original file to array line[n]
set i=0
for /f "usebackq delims=" %%a in ("%filePath%") do (
set /a i+=1
set "line[!i!]=%%a"
)
:: Rename original file with .backup extension
ren "%filePath%" "%fileName%.backup"
:: Rewrite a new file with the name of the original
echo !line[1]!>"%filePath%"
for /l %%i in (2,1,%i%) do echo !line[%%i]!>>"%filePath%"
Common Lisp
Appends a version number and increments it on each backup
(defun parse-integer-quietly (&rest args)
(ignore-errors (apply #'parse-integer args)))
(defun get-next-version (basename)
(flet ((parse-version (pathname)
(or (parse-integer-quietly
(string-left-trim (file-namestring basename)
(file-namestring pathname))
:start 1) 0)))
(let* ((files (directory (format nil "~A,*" (namestring basename))))
(max (reduce #'max files :key #'parse-version)))
(merge-pathnames (format nil "~a,~d" (file-namestring basename) (1+ max))
basename))))
(defun save-with-backup (filename data)
(let ((file (probe-file filename)))
(rename-file file (get-next-version file))
(with-open-file (out file :direction :output)
(print data out))))
Elixir
defmodule RC do
def backup_file(filename) do
backup = filename <> ".backup"
case File.rename(filename, backup) do
:ok -> :ok
{:error, reason} -> raise "rename error: #{reason}"
end
File.cp!(backup, filename)
end
end
hd(System.argv) |> RC.backup_file
Go
Rename
This is the technique of the task description.
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
fn := "myth"
bx := ".backup"
// see if it's a link
var err error
if tf, err := os.Readlink(fn); err == nil {
fn = tf
}
// stat to preserve permissions.
var fi os.FileInfo
if fi, err = os.Stat(fn); err != nil {
fmt.Println(err)
return
}
// attemp rename
if err = os.Rename(fn, fn+bx); err != nil {
fmt.Println(err)
return
}
// create new file
err = ioutil.WriteFile(fn, []byte("you too!\n"), fi.Mode().Perm())
if err != nil {
fmt.Println(err)
}
}
Copy
Alternative technique copies an existing file to make the backup copy, then updates the origial file. In an attempt to keep operations atomic, the original file is opened as the first operation and is not closed until all operations are complete. In an attempt to avoid data loss, the original file is not modified until the backup file is closed.
package main
import (
"fmt"
"io"
"os"
)
func main() {
err := updateWithBackup("myth", ".backup", func(f *os.File) (err error) {
if _, err = f.Seek(0, os.SEEK_SET); err != nil {
return
}
if err = f.Truncate(0); err != nil {
return
}
_, err = f.WriteString("you too!\n")
return
})
if err != nil {
fmt.Println(err)
}
}
// updateWithBackup opens fn, creates a backup copy named fn+bx, then passes
// the still-open original file to the supplied function up. up should
// update the file as needed and return any error, but should not close
// the file. updateWithBackup will then close the file and return any error.
func updateWithBackup(fn, bx string, up func(*os.File) error) (err error) {
var f *os.File
if f, err = openWithBackup(fn, bx); err != nil {
return
}
err = up(f)
if cErr := f.Close(); err == nil {
err = cErr
}
return
}
// openWithBackup opens fn, creates a backup copy, and returns fn still open.
// If fn is a symlink, the destination file is opened instead. The name of
// the backup file will be fn+bx, or if fn was a symlink, the name of the
// destination file + bx. Any error encountered is returned. tf will be
// an open file if and only if err == nil.
func openWithBackup(fn, bx string) (tf *os.File, err error) {
// follow symlink.
if target, err := os.Readlink(fn); err == nil {
fn = target
}
// open the target file for exclusive access.
if tf, err = os.OpenFile(fn, os.O_RDWR, 0); err != nil {
return
}
// deferred function closes target file if an error happens
// during the backup operation.
defer func() {
if err != nil {
tf.Close()
}
}()
// stat to preserve permissions.
var fi os.FileInfo
if fi, err = tf.Stat(); err != nil {
return
}
// create backup file, silently droping any existing backup.
var bf *os.File
if bf, err = os.OpenFile(fn+bx, os.O_RDWR|os.O_CREATE|os.O_TRUNC,
fi.Mode().Perm()); err != nil {
return
}
// copy contents. hold on to any error.
_, err = io.Copy(bf, tf)
// do your best to close backup file whether copy worked or not.
if cErr := bf.Close(); err == nil {
err = cErr // return close error if there has been no other error.
}
// backup complete (as long as err == nil)
return
}
Java
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.*;
public class Backup {
public static void saveWithBackup(String filename, String... data)
throws IOException {
//toRealPath() follows symlinks to their ends
Path file = Paths.get(filename).toRealPath();
File backFile = new File(filename + ".backup");
if(!backFile.exists()) {
// ensure the backup file exists so we can write to it later
backFile.createNewFile();
}
Path back = Paths.get(filename + ".backup").toRealPath();
Files.move(file, back, StandardCopyOption.REPLACE_EXISTING);
try(PrintWriter out = new PrintWriter(file.toFile())){
for(int i = 0; i < data.length; i++) {
out.print(data[i]);
if(i < data.length - 1) {
out.println();
}
}
}
}
public static void main(String[] args) {
try {
saveWithBackup("original.txt", "fourth", "fifth", "sixth");
} catch (IOException e) {
System.err.println(e);
}
}
}
Contents of 'original.txt' before the program is run and of 'original.txt.backup' after it is run:
first second third
Contents of 'original.txt' after the program is run:
fourth fifth sixth
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
public class Backup{
public static void saveWithBackup(String filename, String... data)
throws IOException{
File orig = new File(filename);
//getCanonicalPath() follows symlinks to their ends
File backup = new File(orig.getCanonicalPath() + ".backup");
orig.renameTo(backup);
PrintWriter output = new PrintWriter(orig);
for(int i = 0; i < data.length; i++) {
output.print(data[i]);
if(i < data.length - 1) {
output.println();
}
}
output.close();
}
public static void main(String[] args) {
try {
saveWithBackup("original.txt", "fourth", "fifth", "sixth");
} catch (IOException e) {
System.err.println(e);
}
}
}
- Output:
Same as version 7+ example.
Julia
targetfile = "pycon-china"
mv(realpath(targetfile), realpath(targetfile) * ".bak")
# "a+" for permissions of reading, writing, creating
open(targetfile, "w+") do io
println(io, "this task was solved during a talk about rosettacode at the PyCon China in 2011")
end
Kotlin
// version 1.1.51
import java.io.File
fun saveWithBackup(fileName: String, vararg data: String) {
val orig = File(fileName)
// canonicalPath follows symlinks to their ends
val backup = File(orig.canonicalPath + ".backup")
orig.renameTo(backup)
val pw = orig.printWriter()
for (i in data.indices) {
pw.print(data[i])
if (i < data.lastIndex) pw.println()
}
pw.close()
}
fun main(args: Array<String>) {
saveWithBackup("original.txt", "fourth", "fifth", "sixth")
}
Contents of 'original.txt' before the program is run and of 'original.txt.backup' after it is run:
first second third
Contents of 'original.txt' after the program is run:
fourth fifth sixth
Lasso
local(file2use = 'input.txt')
// create file
// only need to do this if this code example has not been run before and input.txt foes not exist, else leave commented out
//local(nf = file(#file2use))
//#nf->doWithClose => { #nf->writeBytes('Hello, World!'->asBytes) }
// move file
local(f = file(#file2use))
local(contents_of_f = #f->readstring)
#f->moveTo(#file2use+'.bak')
#f->close
// make mods to file contents
#contents_of_f->append('\rLasso is awesome!')
// create new file with new contents
local(nf = file(#file2use))
#nf->doWithClose => { #nf->writeBytes(#contents_of_f->asBytes) }
Locomotive Basic
AMSDOS has automatic one-level backups which also work from Locomotive BASIC: If e.g. the file test.bas is saved, the data gets written to test.$$$. When the file is closed a preexisting test.bas gets renamed to test.bak and finally test.$$$ is renamed to test.bas. (These backups affect all file types, not just BASIC source code.)
Nim
In case the backup cannot be done, an exception is raised.
import os, strutils
const
Suffix = ".backup"
Dir = "working_dir"
SubDir = "dir"
f1 = "f1.txt"
f2 = "f2.txt"
f3 = SubDir / "file.txt"
proc newBackup(path: string): string =
## Create a backup file. Return the path to this file.
if not path.fileExists():
raise newException(IOError, "file doesn't exist.")
let path = path.expandFilename() # This follows symlinks.
result = path & Suffix
moveFile(path, result)
result = result.relativePath(getCurrentDir())
# Prepare test files.
let oldDir = getCurrentDir()
createDir(Dir)
setCurrentDir(Dir)
createDir(SubDir)
f1.writeFile("This is version 1 of file $#" % f1)
f3.writeFile("This is version 1 of file $#" % f3)
createSymlink(f3, f2)
# Display initial state.
echo "Before backup:"
echo f1, ": ", f1.readFile
echo f2, " → ", f3, ": ", f2.readFile()
# Create backups.
echo "\nBackup of regular file:"
let f1Backup = newBackup(f1)
f1.writeFile("This is version 2 of file $#" % f1)
echo f1, ": ", f1.readFile()
echo f1Backup, ": ", f1Backup.readFile()
echo "\nBackup of symbolic link to file:"
let f2Backup = newBackup(f2)
f2.writeFile("This is version 2 of file $#" % f3)
echo f2, " → ", f3, ": ", f2.readFile()
echo f2Backup, ": ", f2Backup.readFile()
# Cleanup.
setCurrentDir(oldDir)
removeDir(Dir)
- Output:
Before backup: f1.txt: This is version 1 of file f1.txt f2.txt → dir/file.txt: This is version 1 of file dir/file.txt Backup of regular file: f1.txt: This is version 2 of file f1.txt f1.txt.backup: This is version 1 of file f1.txt Backup of symbolic link to file: f2.txt → dir/file.txt: This is version 2 of file dir/file.txt dir/file.txt.backup: This is version 1 of file dir/file.txt
Perl
use strict;
use warnings;
sub backup {
my($filepath,$limit,$ext) = @_;
my $abs = readlink $filepath // $filepath; # always resolve symlinks
for my $bnum (reverse 1 .. $limit-1) {
rename "$abs$ext$bnum", "$abs$ext" . ++$bnum if -e "$abs$ext$bnum";
}
if (-e $abs) {
if ($limit > 0) {
my $orig = $abs . $ext . '1';
rename $abs, $orig;
open(IN, '<', $orig) or die "can't open $orig: $!";
open(OUT, '>', $abs) or die "can't open $abs: $!";
my $blksize = (stat IN)[11] || 2**14; # preferred block size?
my $buf;
while (my $len = sysread IN, $buf, $blksize) {
die "System read error: $!\n" if !defined $len;
my $offset = 0;
while ($len) { # Handle partial writes.
defined(my $written = syswrite OUT, $buf, $len, $offset)
or die "System write error: $!\n";
$len -= $written;
$offset += $written;
};
}
close(IN);
close(OUT);
}
} else {
warn "File not found: $abs" and return 0;
}
$abs
}
# back up this program
backup($0,3,'.bk');
Phix
targetfile = get_proper_path("test.txt")
if not rename_file(targetfile, targetfile&".bak", overwrite:=true) then
puts(1,"warning: could not rename file\n")
end if
integer fn = open(targetfile,"w")
if fn=-1 then
puts(1,"error: cannot open file for writing\n")
else
puts(fn,"this task was translated from the Python entry\n")
close(fn)
end if
Before basing anything on the above code, though, I would recommend you take a look at
function saveFile in demo\edix\edix.exw, which does this sort of thing for real:
test.txt -> test.0001.txt, test.0002.txt, etc, in subdirectories \backups, \backups.0001, \backups.0002, etc.
PicoLisp
PicoLisp makes use of external commands as much as possible (at least for not time-critical operations), to avoid duplicated functionality.
(let Path (in '(realpath "foo") (line T))
(call 'mv Path (pack Path ".backup"))
(out Path
(prinl "This is the new file") ) )
Pike
string targetfile = "pycon-china";
targetfile = System.resolvepath(targetfile);
mv(targetfile, targetfile+"~");
Stdio.write_file(targetfile, "this task was solved at the pycon china 2011");
Python
Using os library
import os
targetfile = "pycon-china"
os.rename(os.path.realpath(targetfile), os.path.realpath(targetfile)+".bak")
f = open(os.path.realpath(targetfile), "w")
f.write("this task was solved during a talk about rosettacode at the PyCon China in 2011")
f.close()
Or using a newer pathlib library (Python >= 3.4):
from pathlib import Path
filepath = Path("original_file")
filepath.rename(filepath.with_suffix('.bak'))
with filepath.open('w') as file:
file.write("New content")
Racket
This version keeps unlimited backups, with *.bak being the freshest one, *.bak1 is an older backup, etc. So each backup moves all existing names up.
#lang racket
(define (move-to-backup file)
(define backup
(regexp-replace #rx"^(.*?)(?:\\.bak([0-9]*))?$" file
(λ (_ base num) (~a base ".bak" (add1 (if num (string->number num) 0))))))
(eprintf "~s -> ~s\n" file backup)
(when (file-exists? backup) (move-to-backup backup))
(rename-file-or-directory file backup)
backup)
(define (revise path0)
(define path (path->string (normalize-path path0))) ; chase symlinks
(when (file-exists? path) (copy-file (move-to-backup path) path))
(display-to-file "Another line\n" path #:exists 'append))
(revise "fff")
Raku
(formerly Perl 6)
# Back up the given path/filename with a default extension .bk(n)
# where n is in the range 1 - $limit (default 3).
# Prints 'File not found' to STDERR if the file does not exist.
# Will not do anything if limit is set to less than 1.
# Will not follow symlinks by default.
sub backup (Str $filepath, Int :$limit = 3, Str :$ext = 'bk', Bool :$follow-symlinks = False) {
my $abs = $follow-symlinks ?? $filepath.IO.resolve.absolute !! $filepath.IO.absolute;
for (1 ..^ $limit).reverse -> $bnum {
if "{$abs}.{$ext}{$bnum}".IO.e {
"{$abs}.{$ext}{$bnum}".IO.rename: "{$abs}.{$ext}{$bnum + 1}";
}
}
if $abs.IO.e {
if $limit > 0 {
$abs.IO.rename: "{$abs}.{$ext}1";
my $in = "{$abs}.{$ext}1".IO.open :r :bin or note $! and return False;
my $out = $abs.IO.open :w :bin or note $! and return False;
my $buffer-size = 32768; # 32Kb
while my $buf = $in.read($buffer-size) { $out.write($buf) };
close $in;
close $out;
}
} else {
note "File not found: $abs" and return False;
}
$abs # return filepath on success
}
# back up this program
backup $*PROGRAM-NAME;
# Optionally, specify limit, back-up extension pattern and whether to follow symlinks.
# Optional parameters can be in any order, in any combination.
backup 'myfile', :follow-symlinks, :limit(2), :ext('bak');
REXX
This REXX version executes under DOS or DOS under Windows.
/*REXX program creates a backup file (for a given file), then overwrites the old file.*/
parse arg oFID . /*get a required argument from the C.L.*/
if oFID=='' then do /*No argument? Then issue an err msg.*/
say '***error*** no fileID was specified.'
exit 13
end
tFID= oFID'.$$$' /*create temporary name for the backup.*/
call lineout oFID /*close the file (in case it's open). */
call lineout tFID /* " " " " " " " */
'ERASE' tFID /*delete the backup file (if it exists)*/
'RENAME' oFID tFID /*rename the original file to backup. */
call lineout oFID, '═══This is line 1.' /*write one line to the original file. */
/*stick a fork in it, we're all done. */
The contents of the original file (before execution): A.FILE:
AA FFFFFFFFFFFF IIIIII LLLL EEEEEEEEEEEE AAAA FFFFFFFFFFFF IIIIII LLLL EEEEEEEEEEEE AA AA FF FF II LL EE EE AA AA FF II LL EE AA AA FF FF II LL EE EE AA AA FFFFFFF II LL EEEEEEE AAAAAAAAAA FFFFFFF II LL EEEEEEE AAAAAAAAAA FF FF II LL LLLL EE EE AA AA FF II LL LLLL EE AA AA FF II LL LL EE EE AAAA AAAA .. FF IIIIII LLLLLLLLLL EEEEEEEEEEEE AAAA AAAA .. FF IIIIII LLLLLLLLLL EEEEEEEEEEEE
The contents of the overwritten file (after execution): A.FILE:
═══This is line 1.
The backup file is called: A.FILE.$$$
Ruby
This version does not overwrite the backup file if it exists.
def backup_and_open(filename)
filename = File.realpath(filename)
bkup = filename + ".backup"
backup_files = Dir.glob(bkup + "*").sort_by do |f|
f.match(/\d+$/)
$&.nil? ? 0 : $&.to_i
end
backup_files.reverse.each do |fname|
if m = fname.match(/\.backup\.(\d+)$/)
File.rename(fname, "%s.%d" % [bkup, m[1].to_i + 1])
elsif fname == bkup
File.rename(bkup, bkup + ".1")
end
end
File.rename(filename, bkup)
File.open(filename, "w") {|handle| yield handle}
end
1.upto(12) {|i| backup_and_open(ARGV[0]) {|fh| fh.puts "backup #{i}"}}
Example:
$ echo "original" > original $ ln -s original linkfile $ ruby backup.rb linkfile $ ls -l linkfile* original* lrwxrwxrwx 1 glennj mkgroup-l-d 8 Nov 11 11:22 linkfile -> original -rw-rw-rw-+ 1 glennj mkgroup-l-d 10 Nov 11 11:41 original -rw-rw-rw-+ 1 glennj mkgroup-l-d 10 Nov 11 11:41 original.backup -rw-rw-rw-+ 1 glennj mkgroup-l-d 10 Nov 11 11:41 original.backup.1 -rw-rw-rw-+ 1 glennj mkgroup-l-d 9 Nov 11 11:41 original.backup.10 -rw-rw-rw-+ 1 glennj mkgroup-l-d 9 Nov 11 11:37 original.backup.11 -rw-rw-rw-+ 1 glennj mkgroup-l-d 9 Nov 11 11:41 original.backup.2 -rw-rw-rw-+ 1 glennj mkgroup-l-d 9 Nov 11 11:41 original.backup.3 -rw-rw-rw-+ 1 glennj mkgroup-l-d 9 Nov 11 11:41 original.backup.4 -rw-rw-rw-+ 1 glennj mkgroup-l-d 9 Nov 11 11:41 original.backup.5 -rw-rw-rw-+ 1 glennj mkgroup-l-d 9 Nov 11 11:41 original.backup.6 -rw-rw-rw-+ 1 glennj mkgroup-l-d 9 Nov 11 11:41 original.backup.7 -rw-rw-rw-+ 1 glennj mkgroup-l-d 9 Nov 11 11:41 original.backup.8 -rw-rw-rw-+ 1 glennj mkgroup-l-d 9 Nov 11 11:41 original.backup.9 $ cat original original.backup original.backup.1 original.backup.2 original.backup.3 original.backup.4 original.backup.5 original.backup.6 original.backup.7 original.backup.8 original.backup.9 original.backup.10 original.backup.11 backup 12 backup 11 backup 10 backup 9 backup 8 backup 7 backup 6 backup 5 backup 4 backup 3 backup 2 backup 1 original
Scala
Java Interoperability
import java.io.{File, PrintWriter}
import java.nio.file.{Files, Paths, StandardCopyOption}
object Backup extends App {
def saveWithBackup(filename: String, data: String*): Unit = { //toRealPath() follows symlinks to their ends
val (file, backFile) = (Paths.get(filename).toRealPath(), new File(filename + ".backup"))
if (!backFile.exists) { // ensure the backup file exists so we can write to it later
backFile.createNewFile
}
val back = Paths.get(filename + ".backup").toRealPath()
Files.move(file, back, StandardCopyOption.REPLACE_EXISTING)
val out = new PrintWriter(file.toFile)
for (i <- 0 until data.length) {
out.print(data(i))
if (i < data.length - 1) out.println()
}
}
saveWithBackup("original.txt", "fourth", "fifth", "sixth")
}
Tcl
package require Tcl 8.5
proc backupopen {filename mode} {
set filename [file normalize $filename]
if {[file exists $filename]} {
set backups [glob -nocomplain -path $filename ,*]
set backups [lsort -dictionary \
[lsearch -all -inline -regexp $backups {,\d+$}]]
if {![llength $backups]} {
set n 0
} else {
set n [regexp -inline {\d+$} [lindex $backups end]]
}
while 1 {
set backup $filename,[incr n]
if {![catch {file copy $filename $backup}]} {
break
}
}
}
return [open $filename $mode]
}
VBA
Public Sub backup(filename As String)
If Len(Dir(filename)) > 0 Then
On Error Resume Next
Name filename As filename & ".bak"
Else
If Len(Dir(filename & ".lnk")) > 0 Then
On Error Resume Next
With CreateObject("Wscript.Shell").CreateShortcut(filename & ".lnk")
link = .TargetPath
.Close
End With
Name link As link & ".bak"
End If
End If
End Sub
Public Sub main()
backup "D:\test.txt"
End Sub
Wren
import "/ioutil" for File, FileUtil
var saveWithBackup = Fn.new { |filePath, lines|
var origPath = filePath
if (File.exists(origPath)) {
origPath = File.realPath(origPath) // gets the canonical absolute path to the file
var backupPath = origPath + ".backup"
FileUtil.move(origPath, backupPath) // follows Linux convention of moving rather than renaming
}
FileUtil.writeLines(origPath, lines) // overwrites the file if it already exists
}
saveWithBackup.call("original.txt", ["fourth", "fifth", "sixth"])
// check it worked
System.print("Current contents of original.txt:")
System.print(File.read("original.txt"))
System.print("Contents of original.txt.backup:")
System.print(File.read("original.txt.backup"))
- Output:
Current contents of original.txt: fourth fifth sixth Contents of original.txt.backup: first second third