Implementing “touch” in Powershell
In PowerShell script, it seems that setting the contents of a new file to String.Empty results in a 2-byte file, not a zero-byte file.
PS C:\psScripts> function touch {set-content -Path ($args[0]) -Value ([String]::Empty) }
PS C:\psScripts> touch x
PS C:\psScripts> ls xDirectory: Microsoft.PowerShell.Core\FileSystem::C:\psScripts
Mode LastWriteTime Length Name
—- ————- —— —-
-a— 4/30/2006 2:01 PM 2 xPS C:\psScripts>
Why?



April 30th, 2006 at 3:05 pm
Look inside the file. It contains CRLF, i.e a line break. Try
function touch {set-content -Path ($args[0]) -Value $null }
April 30th, 2006 at 4:45 pm
Seems it adds a Cr/Lf to the file
April 30th, 2006 at 4:50 pm
Yep. Here’s a working version of touch that makes a zero-length file:
function touch {set-content -Path ($args[0]) -Value ($null) }Enjoy.
April 30th, 2006 at 5:04 pm
Here is a tweaked version from the Monad Script Centre.
The echo command which is aliased to Write-Object created a 6 byte file when passed and empty string.
http://www.reskit.net/Monad/samplescripts.htm
function touch-file { param([string] $file)
if (test-path $file)
{ “File $file already exists”; return
}
else
{ echo $null > $file; #pipe default contents
echo “$file created”; #let user know
}
}
set-alias touch touch-file
May 1st, 2006 at 12:28 am
Ummm … not to be picky but traditionally touch is used to update a files modification timestamps, and only if the particular file did not exist would touch create an empty file. So that function should be more like:
function touch-file { param([string] $fil)
if (test-path $fil)
{ $now = [DateTime]::Now
sp -path $fil -name LastWriteTime -value $now
“File $fil timestamp updated”; return
}
else
{
echo $null > $fil;#pipe default contents
echo “$fil created”; #let user know
}
}
Now I had a little trouble with inlining the DateTime::Now into the sp statement. Any hints?
May 1st, 2006 at 12:47 am
Yeah good point. I was really working on just the create part, not the update part, but you make a valid point. Plus I like how you’ve named it consistent with the shell’s verb-noun convention.
As to inlining DateTime::Now, the trick is to enclose in parentheses. As per the PowerShell User Guide, “To mix expressions and commands, you must use parentheses. Inside parentheses, the mode discovery process starts over.”.
Last I’ve removed the use of aliases in your script. It seems a bad idea, what if the alias changes? I wonder what the PS team thought about that idea, since aliases seem so basic to much of what happens. ..?
Anywyay, combining all this, we have :
function touch-file {
param([string] $fil)
if (test-path $fil)
{
Set-ItemProperty -Path $fil -Name LastWriteTime -Value ([DateTime]::Now)
“File $fil timestamp updated”; return
}
else
{
Set-Content -Path ($fil) -Value ($null);
“File $fil created”;
}
}
Thanks, man!
May 1st, 2006 at 9:01 am
([DateTime]::Now) can also be expressed as (Get-Date)
May 1st, 2006 at 3:11 pm
From Keith Hill’s Blog, a better version of touch-file.
This version can be used serveral ways:
PS C:\> touch-file foo.txt
PS C:\> touch-file *.bak
PS C:\> gci . -rec -fil “*.cs” | touch-file
function touch-file([string[]]$paths) {
begin {
function updateFileSystemInfo([System.IO.FileSystemInfo]$fsInfo) {
$datetime = get-date
$fsInfo.CreationTime = $datetime
$fsInfo.LastWriteTime = $datetime
$fsInfo.LastAccessTime = $datetime
}
function touchExistingFile($arg) {
if ($arg -is [System.IO.FileSystemInfo]) {
updateFileSystemInfo($arg)
}
else {
$resolvedPaths = resolve-path $arg
foreach ($rpath in $resolvedPaths) {
if (test-path -type Container $rpath) {
$fsInfo = new-object System.IO.DirectoryInfo($rpath)
}
else {
$fsInfo = new-object System.IO.FileInfo($rpath)
}
updateFileSystemInfo($fsInfo)
}
}
}
function touchNewFile([string]$path) {
$null > $path
}
}
process {
if ($_) {
if (test-path $_) {
touchExistingFile($_)
}
else {
touchNewFile($_)
}
}
}
end {
if ($paths) {
foreach ($path in $paths) {
if (test-path $path) {
touchExistingFile($path)
}
else {
touchNewFile($path)
}
}
}
}
}