Implementing “touch” in Powershell

April 30th, 2006

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 x

Directory: Microsoft.PowerShell.Core\FileSystem::C:\psScripts

Mode LastWriteTime Length Name
—- ————- —— —-
-a— 4/30/2006 2:01 PM 2 x

PS C:\psScripts>

Why?

8 Responses to “Implementing “touch” in Powershell”

  1. Martin Says:

    Look inside the file. It contains CRLF, i.e a line break. Try

    function touch {set-content -Path ($args[0]) -Value $null }

  2. Doug Finke Says:

    Seems it adds a Cr/Lf to the file

  3. Daniel Chait Says:

    Yep. Here’s a working version of touch that makes a zero-length file:

    function touch {set-content -Path ($args[0]) -Value ($null) }

    Enjoy.

  4. Doug Finke Says:

    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

  5. Manung Han Says:

    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?

  6. Daniel Chait Says:

    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!

  7. Doug Says:

    ([DateTime]::Now) can also be expressed as (Get-Date)

  8. Doug Says:

    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)
    }
    }
    }
    }
    }