Powershell: Adicione suporte para pastas especiais (pastas conhecidas) por meio de um provedor / namespace separado

Criado em 31 mai. 2018  ·  74Comentários  ·  Fonte: PowerShell/PowerShell

O sistema de arquivos tem várias pastas especiais (também conhecidas como pastas conhecidas): geralmente pastas preexistentes que têm um significado especial para o sistema operacional.

Seria conveniente ser capaz de direcioná-los no abstrato - tanto por conveniência quanto para abstração de plataforma - semelhante ao que o .NET fornece; por exemplo:

# E.g., on Windows:
PS> [Environment]::GetFolderPath('Desktop')
C:\Users\jdoe\Desktop

'Desktop' traduz em um valor enum da enumeração [Environment+SpecialFolder] .

Suportar esses valores enum por meio de um novo parâmetro -SpecialFolder (alias -sf ) nativamente no PowerShell seria útil:
(Exemplo removido.)

_Update_: A melhor maneira de revelar essa funcionalidade é por meio de uma sf: ou SpecialFolder: _drive_, que habilitaria a seguinte sintaxe:

Set-Location $sf:Desktop  # wishful thinking: changes to, e.g., C:\Users\jdoe\Desktop
$sf:Desktop  # wishful thinking; returns that path; short for: Get-Content sf:Desktop

Dados ambientais

Escrito a partir de:

PowerShell Core v6.1.0-preview.2
Area-Cmdlets-Management Committee-Reviewed Issue-Enhancement Up-for-Grabs WG-Engine-Providers

Comentários muito úteis

Se sf: é uma unidade de arquivo, por que deveria funcionar diferente de Temp: ?

sf: me lembra o conceito de Biblioteca do Windows - coletar pastas em uma entidade.

Todos 74 comentários

Parece que usar um prefixo para pastas especiais é melhor UX do que parâmetro. Não podemos usar ~ , talvez ~~MyPictures ?

@iSazonov :

Eu sei que -SpecialFolder (ou -SpecialDirectory , visto que o PowerShell usa o termo _diretório_) é extenso, mas o alias -sf atenuaria isso.

~~MyPictures é, pelo menos hipoteticamente, uma alteração importante, porque nada o impede de criar uma pasta chamada literalmente ~~MyPictures .
(Observe que shells do tipo POSIX suportam ~<username> como um atalho para se referir à pasta pessoal de um usuário diferente (mas apenas se usado _não cotado_), mas é um recurso obscuro que não acho que vale a pena adotar no PowerShell. )

Mais geralmente, o significado de ~ é relativo ao provedor subjacente à _localização atual_ e, portanto, geralmente não é adequado para referir-se incondicionalmente a uma localização do sistema de arquivos.

Uma solução mais parecida com PowerShell seria:

  • Implementar um provedor de unidade SpecialFolder que expõe, digamos, unidade sf: ,

  • que então permitiria que você use notação de namespace para acessar seus itens; por exemplo,
    Set-Location $sf:MyPictures

Além disso, cd ~\pictures é menos para digitar. Idem com cd ~\desktop . Com o preenchimento da guia, há ainda menos para digitar cd ~\de<tab> .

@rkeithhill :

Sim, mas (a) já funciona e (b) apenas se o local atual for um local do sistema de arquivos.

Enquanto estiver em um local de sistema de arquivos, ~ é um atalho conveniente (que, infelizmente, não é totalmente equivalente semanticamente com (não citado) ~ em shells semelhantes a POSIX, como bash due à sua sensibilidade à localização atual).

Deixando de lado a limitação declarada, você pode, portanto, pensar em ~ como a abreviação de _um_ pasta especial no PowerShell que já está disponível e para outras pastas especiais que _parecem estar_ na _subárvore_ da pasta inicial, algo como ~/Desktop funciona bem.

No entanto, assim como ~ é uma abstração útil - pode se referir a, digamos, C:\Users\jdoe , /home/jdoe , ou /Users/jdoe , por exemplo - tendo abstrações para outras pastas conhecidas são igualmente úteis e é aí que entram as pastas especiais.

Por exemplo, a pasta especial ApplicationData é algo como C:\Users\jdoe\AppData\Roaming no Windows e $env:HOME/.config em plataformas do tipo Unix.

Mesmo ~ tem suas limitações conceituais: como @iSazonov apontou em https://github.com/PowerShell/PowerShell/issues/6791#issuecomment -393465147, você pode pensar em ~ em _Unix_ correspondendo a ~\Documents no _Windows_, visto que o Windows desencoraja a criação de conteúdo do usuário diretamente na pasta de perfil;
Ao contrário de ~ , a pasta especial MyDocuments (alias: Personal ) fornece exatamente esta abstração: ela retorna o equivalente a ~ ( $HOME ) no Unix e ~/Documents no Windows.

A abreviatura ~ não se adapta bem: algo como ~~MyDocuments e ~MyDocuments , conforme declarado, é uma alteração importante, mas, talvez mais importante, seria contraditória no caso de pastas _independentes do usuário_, como CommonApplicationData (que é C:\ProgramData no Windows e /usr/share no Unix).

mas que (a) já funciona

Sim, estou questionando o valor do recurso proposto.

a pasta especial ApplicationData é algo como C: UsersjdoeAppDataRoaming no Windows e $ env: HOME / .config em plataformas do tipo Unix.

Tentar mapear locais orientados para Windows para * nix não parece uma boa ideia para mim.

~~ MyPictures é, pelo menos hipoteticamente, uma alteração importante, porque nada o impede de criar uma pasta chamada literalmente ~~ MyPictures.

Por que para? Podemos usar escape e LiteralPath.
Parece que no Unix o ~ também é um nome válido. E todas as outras opções também.
Acho que podemos aprimorar ~ e usar ~ CommonApplicationData.

Tentar mapear locais orientados para Windows para * nix não parece uma boa ideia para mim.

Isso já foi implementado no CoreFX e PowerShell Core.

Isso já foi implementado no CoreFX e PowerShell Core.

Bem, exceto que muitos desses valores de enumeração não fazem sentido no Linux / macOS. Como tal, a maioria dessas enumerações resulta em resultados de string vazia:

17:16ms>  $ht = @{}; [Enum]::GetValues('System.Environment+SpecialFolder') | % { $ht[$_] = [Environment]::GetFolderPath($_) }; $ht

Name                           Value
----                           -----
CDBurning
CommonOemLinks
LocalizedResources
Resources
CommonVideos
CommonPictures
CommonMusic
AdminTools
CommonAdminTools
CommonDocuments
CommonTemplates
CommonProgramFilesX86
CommonProgramFiles
ProgramFilesX86
SystemX86
UserProfile                    /home/hillr
MyPictures
ProgramFiles
System
Windows
CommonApplicationData          /usr/share
History
Cookies
InternetCache
LocalApplicationData           /home/hillr/.local/share
PrinterShortcuts
ApplicationData                /home/hillr/.config
CommonDesktopDirectory
CommonStartup
CommonPrograms
CommonStartMenu
Templates
Fonts
NetworkShortcuts
MyComputer
DesktopDirectory
MyVideos
MyMusic
StartMenu
SendTo
Recent
Startup
Favorites
MyDocuments                    /home/hillr
Programs
Desktop

Conseqüentemente, este seria um IMO de recurso centrado no Windows.

@rkeithhill :

Sim, estou questionando o valor do recurso proposto.

Em uma meta nota: se opor-se à natureza multiplataforma do recurso foi seu argumento para começar, posso pedir que você corte o "comentário do meio" no futuro? "Além disso, cd ~\pictures é menos para digitar ..." não transmite bem esse argumento, e minha longa resposta não teria sido necessária (embora seja de interesse para o quadro maior).

Como sugere a declaração de @iSazonov, a CoreFx vê valor no recurso, visto que foi tomada uma decisão explícita de incluí-lo em plataformas não Windows:

É útil pelas mesmas razões que sempre foi útil, mesmo quando era apenas para Windows, trabalhando em versões e variações de configuração do Windows: ele fornece _bitrações confiáveis_, visto que caminhos embutidos em código podem falhar.

Em um mundo _cross-platform_, abstrações são ainda mais importantes para se referir a _locais conceitualmente equivalentes_ , SE tais equivalentes existirem, porque a determinação de caixa especial do valor para cada plataforma em código de plataforma cruzada é obviamente muito complicada.

Uma conversa relacionada foi a necessidade de uma versão abstrata da plataforma da variável de ambiente %TEMP% Windows - consulte # 4216
(Mais uma vez, o CoreFx liderou o caminho com [System.IO.Path]::GetTempPath() . Como um aparte: indiscutivelmente, o local dos arquivos temporários também deve ser uma pasta especial, o que atualmente não é.)

Bem, exceto que muitos desses valores de enumeração não fazem sentido no Linux / macOS.

É verdade que muitas pastas especiais só fazem sentido no Windows, mas, felizmente, CoreFx já fez o trabalho para nós, identificando aquelas que _fazem_ fazer sentido em todas as plataformas : retornar uma _cadeia vazia_ é [Environment]::GetFolderPath() a maneira de nos dizendo que locais equivalentes _não_ existem.

Ao criar código de plataforma cruzada, não há como evitar saber qual subconjunto faz sentido.

@rkeithhill

Conseqüentemente, este seria um IMO de recurso centrado no Windows.

De fato, e uma vez que não surgiu muito em mais de uma década de _Windows_ PowerShell (foi proposto, mas não houve muito interesse), não tenho certeza se agrega muito valor. A maioria das pastas interessantes estão mais convenientemente disponíveis off ~. Mas suponho que não doeria.

@ mklement0

Enquanto estiver em uma localização de sistema de arquivos, ~ é um atalho conveniente (que, infelizmente, não é totalmente semanticamente equivalente com (não citado) ~ em shells do tipo POSIX, como bash devido à sua sensibilidade à localização atual).

De que forma não é semanticamente equivalente? Você quer dizer o fato de que o leva ao local de origem na unidade atual?

você pode pensar em ~ no Unix correspondendo a ~ Documentos no Windows, visto que o Windows desencoraja a criação de conteúdo do usuário diretamente na pasta de perfil;

Hoje em dia, a maioria das distros Linux, bem como MacOS, usam a mesma estrutura de pastas do Windows, com diretório Documentos, Imagens, Filmes, etc. Portanto, ~ em * sh e ~ em PowerShell são praticamente equivalentes.

@BrucePay :

De fato, e uma vez que não surgiu muito em mais de uma década do Windows PowerShell (foi proposto, mas não houve muito interesse)

No Windows, normalmente você poderia se safar com caminhos (semi) embutidos em código.
Em um mundo de plataforma cruzada, isso não é mais uma opção, portanto, as abstrações de pasta especial se tornam mais valiosas.

De que forma não é semanticamente equivalente? Você quer dizer o fato de que o leva ao local de origem na unidade atual?

Sim: se você tem experiência em Unix e espera que cd ~\Desktop _sempre_ leve você para $HOME\Desktop , ficará surpreso:

PS> Set-Location Alias:
PS Alias:/> cd ~\Desktop  # boom
cd : Home location for this provider is not set. To set the home location, call "(get-psprovider 'Alias').Home = 'path'".
...

Hoje em dia, a maioria das distros Linux, bem como MacOS, usam a mesma estrutura de pastas do Windows, com diretório Documentos, Imagens, Filmes, etc. Portanto, ~ em * sh e ~ em PowerShell são praticamente equivalentes.

Felizmente, sim (embora CoreFx tenha escolhido considerar apenas ~ o equivalente ao Windows ~/Documents via [Environment]::GetFolderPath('MyDocuments' , mas isso não afetaria o uso explícito de ~/Documents )

No entanto, como está implícito acima, os scripts devem usar apenas $HOME , não ~ , para fazer referência _confiável_ à pasta pessoal do usuário.

@iSazonov :

Por que para? Podemos usar escape e LiteralPath.

Ao contrário de shells semelhantes a POSIX, onde _shell_ expande ~ , e _somente se não cotado_, ~ no PowerShell _é_ um caminho literal e é _até o provedor de destino_ interpretá-lo; as seguintes afirmações são todas equivalentes:

Set-Location ~    # implied -Path
Set-Location '~'  # ditto, with quoting
Set-Location -LiteralPath ~
Set-Location -LiteralPath '~'

Acho que a ideia de um PSDrive especial é muito boa

@ mklement0

Sim: Se você tem experiência em Unix e espera que o cd ~ Desktop sempre o leve para $ HOMEDesktop, você ficará surpreso:

Vindo de um passado Unix / POSIX, admito que esta foi uma decisão que não me entusiasmou. Pensamos em alterá-lo algumas vezes, mas não o fizemos. Eu gostaria que tivéssemos feito isso para 6.0.

Ao contrário de shells tipo POSIX, onde o shell se expande ~

Verdadeiro. Existe uma diferença nas camadas e responsabilidades. A vantagem é que isso nos permite globalizar coisas como processos sem precisar fazer aspas.

No Windows, normalmente você poderia se safar com caminhos (semi) embutidos em código.
Em um mundo de plataforma cruzada, isso não é mais uma opção, portanto, as abstrações de pasta especial se tornam mais valiosas.

Todo o motivo para pastas especiais no Windows é porque wasn't possível ter caminhos de código fixo. Por exemplo, costumava ser recomendado que você tenha seus documentos em uma unidade diferente do sistema. Esse tipo de coisa precisava de redirecionamento. O Unix, por outro lado, tem sido bastante consistente com a nomenclatura de diretório (/ etc é sempre / etc). E as várias distribuições parecem ter se estabelecido mais ou menos no mesmo conjunto de diretórios de usuários.

Felizmente, sim (embora CoreFx tenha optado por considerar apenas ~ o equivalente do Windows ~ / Documents via [Environment] :: GetFolderPath ('MyDocuments', mas isso não afetaria o uso explícito de ~ / Documents).

Eu não entendi bem isso. ~ processamento de ~ .

@iSazonov

Não podemos usar ~, talvez ~~ MyPictures?

Na verdade, essa é basicamente a proposta que @JamesWTruher e eu tínhamos na V1. ~/<directory> teria sido relativo ao diretório inicial, mas ~documents , ~ApplicationData etc. teriam sido as pastas especiais, e algo como ~ApplicationData/npm seria um especial -caminho relativo da pasta. O bom dessa abordagem é que você pode permitir atalhos definidos pelo usuário, por exemplo, ~src ou ~test/scripts .

Hoje em dia, a maioria das distros Linux, bem como MacOS, usam a mesma estrutura de pastas do Windows, com diretório Documentos, Imagens, Filmes, etc. Portanto, ~ em * sh e ~ em PowerShell são praticamente equivalentes.

Portanto, espero que o CoreFX mapeie o nome da pasta especial de forma mais ampla. Qualquer reprodutor de música portado pode usar a pasta especial "MyMusic" em qualquer plataforma.

Outras idéias sobre ~.

  • Não gosto que haja conflito com o usuário Unix ~. Podemos usar outro prefixo?
  • Se olharmos os aplicativos do Windows como SCCM (gerenciador de configuração do centro do sistema) ou GPP (preferências de política de grupo), eles usam o formato% specialfolder% internamente.
    CoreFX tem Environment.ExpandEnvironmentVariables para expandir as variáveis ​​de ambiente e
    Environment.GetFolderPath() para expandir nomes de pastas especiais nos caminhos.
    Também em cmd.exe, dir %temp% funciona, mas não no PowerShell.
    Queremos que ambas as expansões funcionem no PowerShell?

@BrucePay :

Por exemplo, costumava ser recomendado que você tenha seus documentos em uma unidade diferente do sistema. Esse tipo de coisa precisava de redirecionamento.

Em uma observação relacionada: parece que o que o PowerShell informa como $HOME é %USERPROFILE% , que é _tipicamente_, mas não necessariamente o mesmo que %HOMEDRIVE%%HOMEPATH% ; o primeiro é a parte móvel do perfil do usuário e o último é para onde vão os _files_ pessoais do usuário.

Se forem diferentes, algo como ~/Desktop não funcionará conforme o esperado.

Portanto, dado que o redirecionamento (locais abstratos cuja instanciação específica da máquina é obtida programaticamente) ainda é uma necessidade no mundo do Windows, nem mesmo ajuda que as distros Linux tenham locais padronizados como ~/Desktop - para operação confiável você ainda precisa do redirecionamento.


Eu não entendi bem isso. ~ o processamento é uma coisa do PowerShell, não do Windows ou CoreFX. Pelo menos não estou ciente de que eles estão fazendo alguma coisa com ~

Eu estava usando ~ meramente como apelido abreviado para $HOME (Unix) e %HOMEDRIVE%%HOMEPATH% (Windows) em meu exemplo (vou chamar ambos de $HOME abaixo) .

Eu estava tentando apontar que se você pedir à API .NET o caminho para os documentos do usuário - [Environment]::GetFolderPath('MyDocuments') :

  • você obtém $HOME _itself_ no Unix
  • você obtém $HOME\Documents no Windows

Em outras palavras: da perspectiva do CoreFx, a localização $HOME/Documents - embora _exista_ em todas as plataformas - não é considerada _equivalente_ entre as plataformas.


Para resumir:

O resultado:

  • Mesmo os scripts somente para Windows se beneficiam do uso de abstrações, porque algo como ~\Desktop ou $HOME\Desktop nem sempre funciona.

  • Os scripts de plataforma cruzada são limitados a um subconjunto relativamente pequeno de pastas especiais.

  • Mais abstrações se tornam disponíveis para scripts voltados para Windows + macOS ou apenas Windows + Linux.

Especificamente, aqui está a lista de pastas especiais definidas em quais plataformas :

  • Windows: Como o recurso foi originado lá, _todas_ as pastas especiais são definidas.

  • Compartilhado - definido em _todas_ plataformas (nota: Desktop e DesktopDir , embora tecnicamente distintos, parecem referir-se ao mesmo local na prática; o primeiro se distingue do último como sendo o "desktop lógico em vez da localização física do sistema de arquivos "- não tenho certeza do que isso significa).

    • ApplicationData
    • CommonApplicationData
    • Desktop
    • DesktopDirectory
    • LocalApplicationData
    • MyDocuments
    • MyMusic
    • MyPictures
    • UserProfile
  • além disso, exclusivo para macOS (não também no Linux)

    • Favorites
    • Fonts
    • InternetCache
    • ProgramFiles
    • System
  • além disso, exclusivo para Linux (não também no macOS)

    • MyVideos
    • Templates

O acima foi obtido com uma versão modificada do comando útil de @rkeithhill :

$ht = @{}; [Enum]::GetValues([Environment+SpecialFolder]) | % { $ht[$_] = [Environment]::GetFolderPath($_) }; $ht.getEnumerator() | ? Value | Sort-Object {[string] $_.key}

Re sintaxe:

Podemos usar outro prefixo?
E algo como ~ApplicationData/npm seria um caminho relativo de pasta especial.

Geralmente, usar um símbolo especial (prefixo) - embora convenientemente conciso - é misterioso (algo que você esperaria do Perl, não do PowerShell), embora se baseie em um símbolo conhecido no mundo _Unix_.

  • Além disso, conforme declarado, dado o significado estabelecido de ~ como a pasta pessoal do _usuário_, algo como ~ProgramFiles - um local independente do usuário - é contraditório.
  • (A sintaxe ~<username> em shells como bash é uma extensão mais natural de ~ , mas não é amplamente usada.)

Da mesma forma, devemos ficar longe de %specialfolder% , visto que se parece com variáveis ​​de ambiente de cmd.exe , enquanto o PowerShell usa $env:<name> para variáveis ​​de ambiente de superfície.

Por outro lado, o uso de _namespace notation_ ainda permite a concisão ao usar expressões idiomáticas estabelecidas do PowerShell; por exemplo:

Set-Location $sf:Desktop  # tab-completion would work too

Implementar um _provider_ apenas para esse propósito pode parecer pesado, mas fazer isso é atualmente um pré-requisito para a notação de namespace.

Portanto, a questão mais ampla é: talvez devêssemos oferecer suporte à definição de namespaces de uma maneira mais leve, sem a necessidade de implementar um provedor de unidade completo?

@ mklement0 Obrigado! Sempre gosto da sua análise.

Eu gosto da ideia. Apenas sem $

Set-Location SpecialFolder:Desktop # full name
Set-Location sf:Desktop  # short name

a questão mais ampla é: talvez devêssemos oferecer suporte à definição de namespaces de uma maneira mais leve, sem a necessidade de implementar um provedor de unidade completo?

Atualmente parece não - isso exigirá a alteração das interfaces internas. Temos um pedido semelhante para fornecedores.
No entanto, poderíamos implementar essa nova unidade ainda hoje :-) - só precisamos do RFC. Foi ótimo se você fez. Parece que vai ser muito curto.

Também não deixaria sem atenção minha pergunta sobre variáveis ​​de ambiente. Eles são freqüentemente usados ​​em outras conchas. Devemos pensar em como usá-los mais facilmente no PowerShell.

Obrigado, @iSazonov; da mesma forma, agradeço seus esforços incansáveis ​​e inúmeras contribuições.

Pensando bem, você está certo: implementar um provedor de drives é o caminho a percorrer, e a solução certa é tornar a _implementação_ mais "leve", ou seja: mais fácil: # 5789.

Quanto a como os itens dessa nova unidade devem ser acessados, deixe-me começar com as variáveis ​​de ambiente, porque a forma como elas são exibidas exemplifica como eu acho que as pastas especiais também devem ser exibidas:

Também não deixaria sem atenção minha pergunta sobre variáveis ​​de ambiente. Eles são freqüentemente usados ​​em outras conchas. Devemos pensar em como usá-los mais facilmente no PowerShell.

Acho que a maneira do PowerShell de expor variáveis ​​de ambiente funciona bem e é suficiente:

  • A unidade env: enumera todas as variáveis ​​de ambiente (por exemplo, (Get-Item Env:PATH).Value relata o valor da variável de ambiente PATH )

  • O recurso de notação de namespace genérico, portanto, permite acessar esses itens usando, por exemplo, $env:PATH ; em outras palavras: $<drive-name>:<item-name> é o equivalente conciso (e presumivelmente mais eficiente) de (Get-Item <drive-name>:<item-name>).Value )

A notação de namespace é uma notação concisa, mas amigável, que suporta o preenchimento de tabulação e até mesmo a incorporação direta em strings expansíveis (sem a necessidade de incluir $(...) ); por exemplo:
"PATH: $env:PATH"

É um conceito poderoso e, embora o uso do namespace $env: seja amplamente difundido - porque é a maneira mais simples de acessar variáveis ​​de ambiente - minha sensação é que muitas pessoas não sabem o quão genérico é o mecanismo subjacente.

Se implementarmos um provedor de unidade SpecialFolders e exibirmos as pastas especiais por meio de uma nova unidade chamada sf: (poderíamos chamá-la de SpecialFolder: , mas observe que não há conceito de nome de unidade aliases, então pelo que entendi, se quiséssemos oferecer suporte a sf: e SpecialFolder: , teríamos que definir _duas_ unidades), acessá-lo por meio de notação de namespace pareceria com o que propus:

Set-Location $sf:Desktop 

Isso realmente eliminaria a necessidade de estender Get-Location , porque Get-Item sf:Desktop Get-Content sf:Desktop seria suficiente - ou simplesmente $sf:Desktop por si só.

Em contraste, a sintaxe que você propõe _break_:

Set-Location sf:Desktop   # !! breaks, if sf: is not a file-system provider drive

Isso tentaria navegar para sf: drive Desktop _container_, mas Desktop não é um container: é um item do tipo _leaf_ análogo a _files_ no provedor de sistema de arquivos. [_update_: veja [abaixo] (https://github.com/PowerShell/PowerShell/issues/6966#issuecomment-663890154)]

(Da mesma forma, Set-Location Env:PATH quebra, como seria uma chamada análoga para todos os fornecedores _não-hierárquicos_ que têm apenas _um_ nível de itens invariavelmente do tipo folha, a saber: Alias: , Function: , Variable: - o único caminho pelo qual você pode navegar nas unidades de tais provedores é a localização _root_; por exemplo, Set-Location Env: .)

Isso pode estar relacionado. Acabei de instalar o PowerShell 6.1.2 e quando eu cd \Users\<user>\Desktop , não consigo criar uma pasta usando mkdir test . Posso com o PowerShell 5.1.17763.124. Isso ocorre com e sem acesso de administrador.

O erro é:

`` ``
▶ $ error [0] | fl * -f

writeErrorStream: True
PSMessageDetails:
Exceção: System.IO.FileNotFoundException: Não foi possível encontrar o arquivo
'C: usersprafulDesktoptest'.
Nome do arquivo: 'C: usersprafulDesktoptest'
em System.IO.FileSystem.CreateDirectory (String fullPath)
em System.IO.Directory.CreateDirectory (String path)
em Microsoft.PowerShell.Commands.FileSystemProvider.Creat
eDirectory (String caminho, Boolean streamOutput)
TargetObject: C: usersprafulDesktoptest
CategoryInfo: WriteError: (C: usersprafulDesktoptest: String)
[New-Item], FileNotFoundException
FullyQualifiedErrorId: CreateDirectoryIOError, Microsoft.PowerShell.Commands.NewItem
Comando
Detalhes do erro :
InvocationInfo: System.Management.Automation.InvocationInfo
ScriptStackTrace: em,: linha 52
em,: linha 1
PipelineIterationInfo: {0, 1}
`` ``

Fazer um diretório em \Users\<user> funciona. Os problemas parecem ser com as pastas especiais (Desktop, Favoritos, Documentos, etc) no Windows 10 1809.

Algo mudou ?!

@Praful Abra um novo problema com etapas de repo.

@ mklement0 A discussão está pronta para a revisão do Comitê do PowerShell? Você poderia resumir a discussão?

Obrigado pelo acompanhamento, @iSazonov : Sim, acho que está pronto; para resumir, a proposta é:

Fornece abstrações de plataforma cruzada convenientes para pastas conhecidas de uma maneira idiomática do PowerShell, revelando a funcionalidade de [Environment]::GetFolderPath() por meio da notação de variável de namespace $sf:<known-folder-name> , onde sf significa _pasta especial_ e <known-folder-name> é um Environment.SpecialFolder enum - por exemplo, $sf:Desktop seria o equivalente a [environment]::GetFolderPath('Desktop')

Nota: Nem todas as pastas conhecidas existem em todas as plataformas - consulte a seção inferior deste comentário para obter detalhes; se não, [Environment]::GetFolderPath() retorna a _cadeia vazia_ (por exemplo, [environment]::GetFolderPath('CDBurning') no macOS); poderíamos espelhar essa prática.

Para esse fim:

  • Implemente um provedor não hierárquico e somente leitura que implemente a interface IContentCmdletProvider e cujos itens sejam nomeados para os valores de enumeração Environment.SpecialFolder e cujo conteúdo seja o valor de passar seu nome para [Environment]::GetFolderPath()

  • Defina uma unidade chamada sf para esse provedor.

Como uma extensão opcional da proposta acima:

  • Aceite um valor enum adicional denominado, digamos, Temp , para retornar o caminho específico da plataforma para a pasta de arquivos temporários que apresentam o valor de [System.IO.Path]::GetTempPath() , PowerShell atualmente não tem analógico - consulte # 4216 ; o suporte para $sf:Temp resolveria isso.

@ SteveL-MSFT O problema está pronto para revisão pelo comitê do PowerShell.

@ PowerShell / powershell-Committee começou a discutir isso, mas não chegou a uma conclusão. Uma proposta que surgiu é uma pasta em env: vez de um novo PSDrive. Portanto, seria semelhante a env:/SpecialFolder/Fonts , por exemplo. Embora SpecialFolders não sejam variáveis ​​de ambiente, são pastas que fazem parte do ambiente do usuário, portanto, conceitualmente, funciona.

Eu não recomendaria isso. Exigir separadores de barra para acessá-los significa que acessá-los como se fossem variáveis ​​(como você pode com $ env: TEMP) se torna complicado. Nem todo mundo está ciente da sintaxe reforçada para variáveis, afinal, é um tipo de nicho.

Parece que a intenção aqui é facilitar o acesso a essas pastas, e não acho que enterrá-lo em uma subárvore especial de Env: faça sentido para esse fim. Todo o resto lá é um único nível, uma matriz plana unidimensional de strings, efetivamente ... A menos que haja outros planos para abstrair esse impulso para aumentar a usabilidade e torná-lo mais robusto como um todo, duvido muito que criar um caso especial aqui vai ajudar em nada.

SpecialFolder-s ainda não são variáveis ​​de ambiente.

@ vexx32 para reiterar algumas das preocupações que tive na semana passada, era realmente em torno de como as pessoas $env em suas cabeças. Para mim (e eu espero que a maior parte da comunidade também) $env e o provedor de ambiente == "variável de ambiente". De acordo com @JamesWTruher , $env deveria conter qualquer informação útil sobre o ambiente, mais parecida com [System.Environment] que incluiria, por direito, as pastas especiais.

Argumentei que a intenção original provavelmente não é clara para os usuários e que faria sentido distinguir de $env de alguma forma, e era mais a favor de algo como $sf , mas isso significa que nós precisa de um PSDrive, ou será uma variável automática. Como @ mklement0 argumentou acima, também achamos que um PSDrive seria um pouco pesado, e é especialmente difícil para mim porque esses itens não são configuráveis. Você está apenas acessando alguns valores somente leitura do sistema.

Se for uma variável automática, então temos que torná-la especial como uma que de alguma forma analisa :foo . De uma perspectiva de "Eu posso ver o que está acontecendo nos bastidores", isso também é complicado. Eu sei que temos muita magia no PowerShell, mas estamos fazendo o nosso melhor para não adicionar mais (deixe as pessoas me chamando por querer magia no PSModulePath RFC :))

Então é por isso que pousamos em um lugar onde queríamos nos mover em direção à intenção original: mover mais informações sobre o meio ambiente para o provedor de meio ambiente. No futuro, poderíamos querer mais além das pastas especiais, e faria sentido criar um precedente para isso agora, então não estamos discutindo por mais provedores somente leitura no futuro.

Além disso, eu diria que este é um recurso muito mais abstração de plataforma cruzada do que facilidade de digitação. Existem várias maneiras de criar um atalho para pastas que você deseja abreviar para suas sessões interativas, mas trata-se mais de fornecer uma maneira para os usuários de PS abstrairem nativamente essas pastas especiais (sejam plataformas cruzadas aquelas que possuem valores definidos no Linux, bem como para mitigar localizações de pastas potencialmente estranhas em ambientes Windows puros).

Por curiosidade, há algum motivo para ninguém ter levantado um cmdlet Get-SpecialFolder ? Existem algumas preocupações de análise versus tempo de execução que surgem se seguirmos esse caminho? (Em que todos poderiam apenas atribuir o alias a gsf ou algo assim, por exemplo, (gsf TEMP) simplesmente funcionar.)

Hm. Um cmdlet tem um pouco mais de sobrecarga do que um provedor ou algo parecido, mas, do contrário, não vejo nada de errado com ele.

Acho que posso entender essa perspectiva com o Ambiente PSProvider. A maior coisa no momento é que ele não parece suportar nada como um nó de contêiner - tentar criar Env:\MyThing\Var no momento (da memória, pelo menos) cria apenas um único item chamado MyThing\Var , não é?

Eu não tenho nenhum problema em _mudar_ isso e fazer funcionar, no entanto. O acesso fácil a pastas especiais seria ótimo! 😄

Para mim (e eu espero que a maior parte da comunidade também) $ env e o provedor de ambiente == "variável de ambiente".

Como resolver isso? Docs diz

Provides access to the Windows environment variables.

E temos um conflito - todas as variáveis ​​env estão na raiz do namespace, embora eu espere:

$env:/specialfolders/
$env:/variables/system/
$env:/variables/user/
$env:/computerinfo/
$env:/osinfo/

ele se parece com / proc no Linux.

@ PowerShell / powershell-Committee analisou isso, também não teríamos problemas com cmdlets para Get-SpecialFolder

@ PowerShell / powershell-Committee analisou isso, também concordaríamos com cmdlets para Get-SpecialFolder

IMO, a abordagem do provedor (ou seja, sf:MyPictures ) tornaria menos digitação.

Isso tentaria navegar para sf: unidade Desktop _container_, mas Desktop não é um contêiner: é um _leaf-type item_ análogo a _files_ no provedor de sistema de arquivos.

TEMP: é um contêiner agora, por que sf:Desktop ser?

Eu falei mal:

A ideia é que $sf:Desktop (que é o equivalente implícito de Get-Content sf:Desktop - não Get-Item sf:Desktop ) retorne diretamente uma _cadeia de caminho nativa_ que representa a pasta Desktop específica da plataforma, para que você pode usá-lo _diretamente_ em argumentos; por exemplo, [IO.File]::CreateText("$sf:Desktop/foo.txt")

Este comportamento é o principal utilitário da sintaxe $sf:Desktop , dado que uma mera string de caminho de provedor, como sf:Desktop _por si mesma_, só é significativa para cmdlets de provedor do _PowerShell_ (ao contrário de caminhos de sistema de arquivos nativos, como C:\Users\Desktop ).

Então, meu pensamento era que os itens da unidade não precisam representar itens de _diretório_ (contêiner), e a única necessidade prática de chamar explicitamente os cmdlets do provedor seria para _discovery_: por exemplo, Get-ChildItem sf: ou Get-Item sf:*music*

Suponho que poderíamos fazer _both_, e também tratar o drive sf: como qualquer outro drive _file-system_ baseado em drive PS, mas há um problema conceitual:

  • Qual local do sistema de arquivos sf:/ - a raiz de todas as pastas especiais - deve representar? Não existe um candidato natural, mas não queremos proibir Set-Location sf:/ completamente, visto que funciona com outros provedores (por exemplo, Set-Location alias:/ funciona perfeitamente); se a localização sf:/ não representa um item real do sistema de arquivos - assim como alias:/ não representa - o problema não surge.

Além disso, $PWD relatará sf:Desktop vez de C:\Users\jdoe\Desktop , por exemplo - como acontece com unidades PS personalizadas.

Qual local do sistema de arquivos My Computer representa? Você pode explorá-lo, pode vinculá-lo, mas não pode passá-lo para um aplicativo que espera um diretório. É a mesma situação.

Não é, porque My Computer ( This PC ) é um conceito do namespace do shell _GUI_ do Windows e seus locais abstratos não podem ser usados ​​diretamente no PowerShell, portanto _o problema não existe atualmente_ - estaríamos apresentando o PowerShell.

(Se você tentar iniciar um console do PowerShell a partir do File Explorer de um local abstrato, como This PC , você obterá set-location : Cannot find path '::{20D04FE0-3AEA-1069-A2D8-08002B30309D}' because it does not exist. e, da mesma forma, não poderá consultar seus filhos com Get-ChildItem .)

Dito isso, não haveria problema em, digamos, mapear a localização sf:/ para a raiz da unidade do sistema ( / no Unix e normalmente C:\ no Windows).

Novamente: o que importa no final do dia é que $sf:Desktop se expande diretamente para o caminho do sistema de arquivos nativo específico da plataforma subjacente ao local abstrato.

De acordo com os provedores existentes, faria mais sentido se SF: fosse mapeado para um local abstrato de onde você pudesse chamar Get-ChildItem e obter uma lista de pastas especiais e seus locais:

PS> Get-ChildItem sf:

Name          Location
----          --------
Desktop       C:\Users\currentUser\Desktop
My Documents  C:\Users\currentUser\My Documents
( ... ) 

[_Update_: Veja [conclusão abaixo] (https://github.com/PowerShell/PowerShell/issues/6966#issuecomment-663910975)]

Sim, @ vexx32 , mas

os itens da unidade não precisam representar itens de diretório (contêiner) e a única necessidade prática de chamar explicitamente os cmdlets do provedor seria para _discovery_: por exemplo, Get-ChildItem sf: ou Get-Item sf:*music*

Isso funcionaria exatamente como Get-ChildItem alias:/ , para a unidade alias: , que também _não_ mapeia para locais do sistema de arquivos.

Para resumir:

  • O que é importante é que a notação de variável de namespace - por exemplo, $sf:Desktop - se expande diretamente para o caminho do sistema de arquivos nativo e específico da plataforma subjacente ao local abstrato.

  • Pessoalmente, estou bem em _também_ tornar a unidade navegável como um provedor especial de _file-system_ (em vez de um provedor que retorna _cadeias de caminho_), mas acho que isso é muito menos importante:

    • Se nós _não_ fizermos de sf: um provedor de sistema de arquivos especial, não se perderá muito: em vez de Set-Location sf:Desktop você terá que usar Set-Location $sf:Desktop (analogamente para Get-Item e Get-ChildItem , se você quiser saída de [System.IO.DirectoryInfo] ), o que não me parece uma dificuldade - embora possa haver potencial para confusão.

    • Se nós _fazermos_ isso:

      • Precisamos decidir como lidar com Set-Location sf:/ , ou seja, navegar até a _localização da raiz_ da unidade, que todos os provedores suportam atualmente.
      • Ou: Evite navegar diretamente para sf:/ , com uma mensagem de erro significativa.
      • Ou: Mapeie sf:/ para locais de sistema de arquivos finalmente arbitrários, como a raiz da unidade do sistema.
      • De qualquer maneira, precisamos estar cientes de que, depois de, digamos, Set-Location sf:Desktop , $PWD seria stringificado como 'sf:/Desktop' , que você não pode usar diretamente (para compor caminhos com) em chamadas que espere caminhos de sistema de arquivos nativos. (Isso se aplica igualmente a unidades PS de sistema de arquivos _personalizadas_, mas criá-las é um ato deliberado, enquanto, neste caso, os usuários podem não esperar esse comportamento; $PWD.ProviderPath é a solução alternativa)

@ mklement0 Acho que ele está apenas respondendo à sua pergunta sobre:

  • Qual local do sistema de arquivos sf:/ - a raiz de todas as pastas especiais - deve representar? Não existe um candidato natural, mas não queremos proibir Set-Location sf:/ completamente, visto que funciona com outros provedores (por exemplo, Set-Location alias:/ funciona perfeitamente); se a localização sf:/ não representa um item real do sistema de arquivos - assim como alias:/ não representa - o problema não surge.

com "não faz, é como alias:/ ".


Também concordo com você que fazer com que o conteúdo da folha seja o caminho como uma string é a única maneira de implementá-lo como um provedor. Vai ser estranho que Get-ChildItem sf:Desktop não funcione como as pessoas esperam. Eu sei que não pode fazer isso com a forma como os provedores funcionam atualmente, mas ainda pode ser muito confuso.

Obrigado, @SeeminglyScience , mas eu estava tentando apontar que apenas se você conceber o provedor subjacente à unidade sf como um provedor de _sistema de arquivos_, você terá que resolver o problema de qual localização do sistema de arquivos sf:/ mapeia para. Portanto, _se_ quisermos que Get-ChildItem sf:Desktop funcione - para evitar confusão - precisamos implementar o provedor como um provedor de sistema de arquivos - e então precisamos resolver esse problema.

Eu realmente não acho que você pode de uma forma que não cause mais confusão. A menos que você apenas coloque em casos especiais todas as resoluções de caminhos começando em sf no próprio FileSystemProvider .

O maior obstáculo em qualquer implementação de provedor que seja "meio como o provedor X, mas com essa única diferença" é que você não pode fazer operações entre provedores. Então Copy-Item sf:\Desktop C:\temp está programado para lançar. Além disso, como a resolução de caminho faz a transição entre provedores como gi sf:\Desktop\Chrome.lnk . A API do provedor não foi criada para lidar com nada disso.

Desculpe se isso tudo já foi mencionado ITT.

Desculpe se isso tudo já foi mencionado ITT.

Não acho que tenha, @SeeminglyScience , então obrigado por esclarecer.

Portanto, parece que a conclusão é:

  • Um provedor subjacente à unidade sf proposta atualmente não pode ser um provedor de sistema de arquivos com caixa especial, portanto, atualmente não podemos oferecer suporte a chamadas como Set-Location sf:Desktop ou Get-Item sf:Desktop ([_update_: while esperando uma instância [System.IO.DirectoryInfo] como saída), mesmo que os usuários possam esperá-la.

  • Como um provedor cujos itens representam simplesmente _cadeias de caminho_, a notação de variável de namespace (por exemplo, $sf:Desktop ) funciona conforme o planejado - que é o que importa - e ainda suporta a descoberta por meio de Get-ChildItem / Get-Item .
    Set-Location sf:/ - na ausência da necessidade de mapear para uma localização do sistema de arquivos - então não apresenta nenhum problema conceitual.

  • Um provedor subjacente à unidade sf proposta atualmente não pode ser um provedor de sistema de arquivos com maiúsculas e minúsculas, portanto, atualmente não podemos oferecer suporte a chamadas como Set-Location sf:Desktop ou Get-Item sf:Desktop , embora os usuários possam espere isso.

Talvez esclareça que Get-Item funcionará, mas não retornará um objeto DirectoryInfo .

Interessante, esperamos que sf: Desktop retorne um valor, mas alias:% não retorna ForEach-Object . Parece um problema fundamental.

@iSazonov ${alias:%} retorna ForEach-Object

Mas queremos Set-Location $ sf: Desktop, não Set-Location $ {sf: Desktop}

$sf:Desktop funcionará bem, as chaves são porque % não é permitido em nomes de variáveis.

>alias:gcm
alias:gcm: The term 'alias:gcm' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

Não é expandido para Get-Command.

alias:gcm é uma palavra de barra, não há nada para expandir aqui.

@iSazonov , isso é esperado, porque alias:gcm não é um caminho de _sistema de arquivos_ que permite _execução direta_. Apenas o alias _provider_ entende este caminho, então você terá que usar Get-Item alias:gcm (ou Get-Content alias:gcm para obter apenas a _definição_ do alias, que é a _string_ 'Get-Command' ).

Para executar um alias por seu caminho de provedor PS (que é obviamente uma forma desnecessariamente tortuosa de fazer isso), você teria que fazer isso (onde $alias:gcm é o equivalente a Get-Content alias:gcm ):

& $alias:gcm

Se permitíssemos a execução direta de variáveis, não precisaríamos de arquivos temporários. No entanto, essas variáveis ​​ainda devem ser nomeadas *.PS1 . alias:gcm não tem essa extensão, portanto, de forma alguma pode ser tratado como um script.

isso é esperado, porque alias: gcm não é um caminho do sistema de arquivos

O que queremos - sf: Desktop ou $ sf: Desktop em Set-Location?

O que queremos - sf: Desktop ou $ sf: Desktop em Set-Location?

A única maneira que pode funcionar com a forma como os provedores funcionam atualmente é $sf:Desktop .

Embora eu queira afirmar novamente que acho que esse design é muito confuso para as pessoas que não sabem por que precisa ser assim. Pessoalmente, acho que isso deve esperar até que as operações de resolução / cópia de caminhos cruzados funcionem.

@SeeminglyScience

Percebo o potencial para confusão, mas não há como dizer se e quando a operação de cópia / resolução de caminho cruzado será implementada (ou há planos específicos?), Então não acho que devemos esperar por isso.

Vejo a principal utilidade do recurso proposto em _compondo strings de caminho via interpolação / concatenação de string_, onde $sf:Desktop é a forma necessária (por exemplo, "$sf:Desktop/subdir" (no entanto, com Join-Path o confusão pode surgir novamente).

Se implementarmos um provedor agora e tornar seus itens _contêineres_ do tipo [System.IO.DirectoryInfo] (e o item _content_ sua propriedade .FullName ), então poderíamos concebivelmente fazer a transição posterior para um provedor de sistema de arquivos especializado totalmente integrado - Ou eu estou esquecendo de alguma coisa?

Pensando bem: isso por si só habilitaria Set-Location sf:Desktop , visto que não há nenhuma operação _cross_-provider acontecendo? em vez disso, você mudaria diretamente para o _caminho do sistema de arquivos nativo_ subjacente ao item - mas isso é indiscutivelmente preferível.

[_Update_: Veja [abaixo] (https://github.com/PowerShell/PowerShell/issues/6966#issuecomment-664603897).]

@ yecril71pl

Se permitíssemos a execução direta de variáveis

Não há nenhuma _variável_ aqui (cujo _valor_ você pode invocar com & ), apenas um _caminho literal_ referindo-se a um item exposto pelo Alias provedor (por meio de seu único e alias: dirigir).

@iSazonov

PowerShell _conceptualmente poderia_ - mas atualmente não aceita - aceita o caminho alias:gcm como um _comando_ para executar, embora a instância System.Management.Automation.AliasInfo o caminho se refere seja por definição um comando (deriva de System.Management.Automation.CommandInfo ). Dado que apenas gcm é suficiente para a invocação, entretanto, não acho que isso seja uma lacuna na prática.
Por curiosidade, @SeeminglyScience : esse problema pode ser resolvido com a forma como os provedores funcionam atualmente?

Por outro lado, para que caminhos como sf:Desktop se tornem cidadãos do sistema de arquivos PS de primeira classe, junto com unidades personalizadas do sistema de arquivos PS, precisaríamos de um _provedor de sistema de arquivos especializado_, que
de acordo com a análise da @SeeminglyScience , um problema que não pode ser resolvido com a forma como os provedores trabalham atualmente.

mas não há como dizer se e quando a operação de cópia / resolução de caminho cruzado será implementada

Sim, eu sei, ainda. Uma rota diferente deve ser seguida.

Se implementarmos um provedor agora e tornar seus itens _contêineres_ do tipo [System.IO.DirectoryInfo] (e o item _content_ sua propriedade .FullName ), então poderíamos concebivelmente mais tarde fazer a transição para um provedor de sistema de arquivos especializado totalmente integrado - Ou eu estou esquecendo de alguma coisa?

Acho que é um desafio maior de compreensibilidade. O objeto Get-Item é um DirectoryInfo , mas é uma folha e o conteúdo é o caminho. Então, mais tarde, faremos com que eles não sejam mais uma folha e lançaremos Get-Content ? Mesmo que fosse intuitivo, fica ainda mais difícil para o usuário determinar por que gci sf:/Desktop ou gi sf:/Desktop/pwsh.lnk não funcionam.

Pensando bem: isso por si só habilitaria Set-Location sf:Desktop , visto que não há nenhuma operação _cross_-provider acontecendo? No entanto, você não mudaria para um local _no drive sf: _; em vez disso, você mudaria diretamente para o _caminho do sistema de arquivos nativo_ subjacente ao item - mas isso é indiscutivelmente preferível.

Nah, você não pode definir sua localização para uma folha.

PowerShell _conceptualmente poderia_ - mas atualmente não aceita - aceita o caminho alias:gcm como um _comando_ para executar, embora a instância System.Management.Automation.AliasInfo o caminho se refere seja por definição um comando (deriva de System.Management.Automation.CommandInfo ). Dado que apenas gcm é suficiente para a invocação, entretanto, não acho que isso seja uma lacuna na prática.
Por curiosidade, @SeeminglyScience : esse problema pode ser resolvido com a forma como os provedores funcionam atualmente?

Bem, & (gi alias:gcm) funciona. Eu imagino que uma versão realmente básica poderia ser conectada via ItemCmdletProvider.InvokeDefaultAction se a alteração de quebra fosse aceita. Mas a execução da mesma maneira que funciona para o provedor do sistema de arquivos pode ser uma grande e velha lata de vermes.

Obrigado, @SeeminglyScience.

O objeto Get-Item é um DirectoryInfo, mas é uma folha

A proposta era torná-lo um _contêiner_, mas, sim, permitir que Get-Content relatasse o _caminho_ do contêiner seria uma anomalia (algo como Get-Content $HOME atualmente falha, assim como ${C:\Windows} , com Unable to get content because it is a directory: )

No entanto, inversamente, isso significa que se esperarmos até que possamos tornar o provedor da unidade sf: um provedor de sistema de arquivos especializado e completo, $sf:Desktop não funcionará - o que leva embora o que considero ser o caso de uso principal: quero ser capaz de usar $sf:Desktop/file.txt e não ser forçado a usar "$(Get-Item sf:Desktop)/file.txt" ou (Get-Item sf:Desktop/file.txt).FullName - o que é complicado e ineficiente .

Portanto, para mim, a solução pragmática é: implemente um provedor cujos itens sejam itens _leaf_ que representam _cadeias de caminho_ - e deixe claro na documentação que os caminhos baseados em sf: não podem servir diretamente como itens do sistema de arquivos.

Bem, & (gi alias:gcm) funciona

Claro, e também & $alias:gcm (embora uma _string_ seja retornada nesse caso), mas a questão era se o desvio via Get-Item / notação de variável de namespace pode ser evitado neste caso.

A questão era principalmente acadêmica, no entanto: dado o benefício insignificante de permitir isso e o potencial para armadilhas, concordo que não vale a pena abordar.

A proposta era torná-lo um _contêiner_, mas, sim, permitir que Get-Content relatasse o _caminho_ do contêiner seria uma anomalia (algo como Get-Content $HOME atualmente falha, assim como ${C:\Windows} , com Unable to get content because it is a directory: )

Isso é novamente muito confuso. Como um aparte, não acho uma boa ideia permitir que um contêiner trabalhe com Get-Content apenas para esta solução alternativa.

No entanto, inversamente, isso significa que se esperarmos até que possamos tornar o provedor da unidade sf: um provedor de sistema de arquivos especializado e completo, $sf:Desktop não funcionará - o que leva embora o que considero ser o caso de uso principal: eu quero poder usar $sf:Desktop/file.txt

Sim, entendo totalmente o caso de uso. Isso ainda vai confundir muito as pessoas.

e não ser forçado a usar "$(Get-Item sf:Desktop)/file.txt" ou (Get-Item sf:Desktop/file.txt).FullName - o que é complicado e ineficiente.

Porém, da mesma forma que você resolve caminhos de sistema de arquivos. Também Resolve-Path .

Portanto, para mim, a solução pragmática é: implemente um provedor cujos itens sejam itens _leaf_ que representam _cadeias de caminho_ - e deixe claro na documentação que os caminhos baseados em sf: não podem servir diretamente como itens do sistema de arquivos.

Se tivesse que ser adicionado como seu próprio provedor, essa seria a única maneira que poderia ser implementado atualmente. Eu não acho que deveria ser, seria muito difícil de entender.

Honestamente, eu prefiro ver isso como um caso especial para a resolução de caminho de FileSystemProvider . Não tenho certeza se vale a pena a complicação extra, mas isso seria pelo menos muito fácil de entender.

Claro, e & $alias:gcm (embora uma _string_ seja retornada nesse caso), mas a questão era se o desvio por meio da notação de variável Get-Item / namespace pode ser evitado neste caso.

Sim, entendi, o resto da frase foi a resposta direta.

Não acho uma boa ideia permitir que um contêiner funcione com Get-Content apenas para essa solução alternativa.

Acordado.

Porém, da mesma forma que você resolve caminhos de sistema de arquivos. Também resolver caminho

Sim, mas isso é complicado para este caso de uso.

Se tivesse que ser adicionado como seu próprio provedor, essa seria a única maneira de implementá-lo atualmente. Eu não acho que deveria ser, seria muito difícil de entender.

Se o uso principal for $sf:Desktop em strings de caminho, não acho que as pessoas pensariam duas vezes sobre isso, assim como não pensam em $env:HOME sendo sustentado por uma operação de provedor que é o equivalente a Get-Content Env:HOME .
Com o preenchimento de tabulação adequado, talvez você nunca precise tocar nos cmdlets do provedor e, se pensar em $sf:Desktop apenas como uma variável especial (valor), Set-Content $sf:Desktop também não é um exagero.

Sim, mas isso é complicado para este caso de uso.

Você poderia elaborar? Você só precisaria fazer isso se estivesse passando pelo caminho para algo que não entendia o PSPath. Caso contrário, ele funcionaria de forma semelhante a um PSDrive.

Se o uso principal for $sf:Desktop em strings de caminho, não acho que as pessoas pensariam duas vezes sobre isso, assim como não pensam em $env:HOME sendo sustentado por uma operação de provedor que é o equivalente a Get-Content Env:HOME .

As variáveis ​​de ambiente não são apenas caminhos, os usuários já pensam nelas como variáveis.

Você poderia elaborar?

Eu quero poder usar $sf:Desktop/file.txt , o que não posso se sf:Desktop é um item de contêiner (diretório).

As variáveis ​​de ambiente não são apenas caminhos, os usuários já pensam nelas como variáveis.

Este é o meu ponto: os usuários podem e devem pensar em $sf:Desktop como variáveis ​​(especiais) que contêm strings de caminho.
Apoiar isso com um provedor é simplesmente um requisito técnico para habilitar essa sintaxe.

Eu quero poder usar $sf:Desktop/file.txt , o que não posso se sf:Desktop é um item de contêiner (diretório).

Certo, eu entendi, mas por que você quer isso em vez de trabalhar na resolução de caminho real.

Por que você precisa fazer:

Copy-Item $sf:Desktop/pwsh.lnk ./pwsh.lnk

Ao invés de

Copy-Item sf:/Desktop/pwsh.lnk ./pwsh.lnk

Este é o meu ponto: os usuários podem e devem pensar em $sf:Desktop como variáveis ​​(especiais) que contêm strings de caminho.
Apoiar isso com um provedor é simplesmente um requisito técnico para habilitar essa sintaxe.

Eu entendo como você deseja que seja visto, mas acho que é um desafio. Apresentar um provedor cujo conteúdo é um caminho do sistema de arquivos como uma string que você passa para um provedor diferente para ser resolvido para um item é inerentemente difícil de entender.

Eu entendo como você _quer_ que seja visto

É mais do que isso: estou propondo que seja _activamente enquadrado desta forma_: para tirar a ênfase do aspecto do provedor _como um detalhe de implementação_, o que evita os problemas conceituais. Em vez disso, devemos enquadrar $sf:* como _variáveis ​​especiais_ que retornam _cadeias de caminho_.

Nesse sentido:

Copy-Item sf:/Desktop/pwsh.lnk ./pwsh.lnk

Não tenho ideia do que sf:/Desktop é (estou bancando o ignorante para fazer uma observação) - tudo que sei é que $sf:Desktop retorna a _cadeia de caminho_ do Desktop apropriada para a plataforma que posso usar como parte de strings de caminho maiores - exatamente como eu usaria $env: notação de variável; por exemplo:

Copy-Item $env:ProgramFiles/foo/bar.exe ./bar.exe

Se sf: é uma unidade de arquivo, por que deveria funcionar diferente de Temp: ?

sf: me lembra o conceito de Biblioteca do Windows - coletar pastas em uma entidade.

Porque isso exigiria que sf: fosse uma unidade no sistema de arquivos. E algumas pessoas não gostam dessa solução por ser muito suja, intrusiva ou qualquer outra coisa. Além disso, cheira a My Computer , que é uma interface do Windows, o que significa que teríamos um assassino na sala 😲

Se permitíssemos a execução direta de variáveis

Não há nenhuma _variável_ aqui (cujo _valor_ você pode invocar com & ), apenas um _caminho literal_ referindo-se a um item exposto pelo Alias provedor (por meio de seu único e alias: dirigir).

Uma variável chamada SCRIPT.PS1 tem um caminho literal de VARIABLE:SCRIPT.PS1 . Portanto, quando passo VARIABLE:/SCRIPT.PS1 como um comando, espero que o script na variável seja executado. Mas isso não é permitido e tenho que colocar o script no sistema de arquivos para poder executá-lo.

@iSazonov

sf: seria uma unidade _PowerShell_, mas não uma unidade PowerShell _file-system_ - atualmente _não_ pode ser a última, pelos motivos técnicos explicados por @SeeminglyScience , mas mesmo se pudesse (não está claro se e quando), iria introduzir mais problemas do que resolver.

(Eu fechei o círculo nisso: a proposta original era _não_ concebê-lo como uma unidade de sistema de arquivos, então discutimos o que seria necessário para fazer isso, agora estou de volta à proposta original).

Assim como alias: ou env: não são unidades de _file-system_, sf: também não o seriam: seria uma unidade que expõe itens que representam _strings_ que por acaso são _file -system path strings_ (assim como muitas variáveis ​​de ambiente no Windows contêm caminhos de sistema de arquivos, como $env:ProgramFiles ).

O objetivo principal da unidade sf: seria _ativar a notação de variável de namespace_, de modo que algo como $sf:Desktop expanda para a _cadeia de caminho_ que é o caminho da área de trabalho do sistema de arquivos nativo apropriado para a plataforma e pode ser usado como está na construção de caminhos mais longos ( $sf:Desktop/file.txt ) e chamadas que esperam caminhos nativos do sistema de arquivos _ tanto no argumento quanto no modo de expressão_.

Assim como a maioria dos usuários raramente, ou nunca, interage com o drive Env: por meio do provedor _cmdlets_ - em vez disso, eles contam com a notação de variável de espaço de nomes como $env:ProgramFiles - eles usariam apenas $sf:Desktop , ou preenchimento com tab para descoberta. (Claro, eles _podem_ usar Get-ChildItem sf: para uma descoberta mais conveniente e sofisticada, se quiserem.)

A única razão técnica que consigo pensar é que SF:/ , ao contrário de TEMP:/ , não corresponderia a nenhum local real, então seria uma unidade virtual com links eficazes dentro, um pouco como \\.\ , que é uma ideia interessante melhor deixada para ser implementada pelo sistema operacional subjacente do que por nós mesmos.

@ mklement0

[...] (nota: Desktop e DesktopDir , embora tecnicamente distintos, parecem referir-se ao mesmo local na prática; o primeiro se distingue do último como sendo o "Desktop lógico, em vez do que a localização física do sistema de arquivos "- não tenho certeza do que isso significa).

  • Desktop
  • DesktopDirectory

_ Observação para os não iniciados : só para ficar confuso, "o shell" se refere a um componente do Windows totalmente diferente do PowerShell ou cmd.exe. Você provavelmente o conhece como explorer.exe, embora, naturalmente, a maior parte dele seja implementado em DLLs ..._

Acho que essa distinção é / era relevante apenas com SHGetSpecialFolderLocation e SHGetFolderLocation , que criam caminhos no namespace do shell (estruturas ITEMIDLIST, também chamadas de PIDLs por algum motivo). CSIDL_DESKTOP daria a você o que está realmente representado em sua área de trabalho; CSIDL_DESKTOPDIRECTORY daria a você o lugar para onde você chega quando digita, por exemplo,% USERPROFILE% Desktop na barra de endereços do Explorer.

[A documentação parece indicar que os dois valores fazem a mesma coisa desde o Vista, mas não vou assumir que isso seja realmente verdade.]

Em particular, CSIDL_DESKTOP teria quaisquer itens extras [Computador, Lixeira, etc.] que o usuário configurou para aparecer na área de trabalho, bem como itens contribuídos de CSIDL_COMMON_DESKTOPDIRECTORY (ou seja, os atalhos da área de trabalho Todos os usuários).

Naturalmente, nada disso é relevante para o valor de retorno de System.Environment.GetFolderPath .

há algum motivo para ninguém ter criado um cmdlet Get-SpecialFolder?

Acabei de apontar para este problema. Todo o assunto certamente merece consideração e design cuidadosos, mas isso talvez possa ajudar alguém agora:

Vários anos atrás, criei um módulo PS para inspecionar e manipular pastas conhecidas usando a API COM (que fornece muito mais funcionalidade do que a API legada usada internamente pela classe de ambiente .NET). Eu queria aliviar minha dor de redirecionar em massa as pastas (incluindo as Públicas) para fora da unidade do sistema ao configurar vários computadores para vários membros da minha família - por padrão, possível apenas clicando na GUI (e exigindo a desativação temporária do UAC mover as pastas públicas). O objetivo era ser capaz de escrever

# as any user
Get-KnownFolder | Move-KnownFolder -Destination F:\Profiles\$Env:UserName
# single folder, with rename
Move-KnownFolder -SingleFolder (Get-KnownFolder -Name Personal) -NewPath D:\Data\$Env:UserName\Docs
# as admin
Get-KnownFolder -Public | Move-KnownFolder -Destination F:\Profiles\Public

O módulo é totalmente funcional (eu costumo usá-lo sempre que configuro um novo computador); Acabei de ficar sem tempo livre e sem motivação para fazer os retoques finais - escrever alguns documentos / exemplos e publicar no PSGallery. Provavelmente poderia fazer esse esforço se houver interesse.

Para inspecionar os caminhos, usaríamos:

PS C:\> (Get-KnownFolder -Name Personal).Path
F:\Profiles\jberezanski\Documents

Minha implementação é obviamente apenas para Windows; Não sei quantos conceitos do Windows Shell são aplicáveis ​​a outras plataformas. Quando pensei em expor mais desses conceitos em PS, tendia a pensar em termos de provedor + drive - Shell:\ , com a raiz representando a raiz do namespace Shell (o Desktop "lógico"). No entanto, não encontrei casos de uso reais para navegar nesse namespace e todas as minhas necessidades práticas foram satisfeitas pelos dois cmdlets mencionados acima.

(Pessoalmente, adoraria ver uma forma de plataforma cruzada para referenciar as pastas AppData / Local AppData conhecidas, de modo que os aplicativos pudessem parar de poluir a raiz do diretório de perfil do usuário com seus dados de configuração arquivos e pastas, mas isso é um discurso totalmente diferente.)

Obrigado, @jberezanski.

Não sei quantos conceitos do Windows Shell são aplicáveis ​​a outras plataformas

Eu resumi quais pastas conhecidas se aplicam a quais plataformas acima .

Embora eu ache que Get-KnownFolder seria uma boa adição (criei uma função com o mesmo nome para mim há algum tempo), acho que algo como $sf:Desktop é _também_ chamado para:

Especificamente, diminuiria a tentação de escrever códigos como 'hi' > $HOME/Desktop/foo.txt , visto que a única formulação totalmente robusta seria a mais detalhada 'hi' > "$(Get-KnownFolder Desktop)/foo.txt" (observe as aspas duplas obrigatórias; um Join-Path command seria ainda mais detalhado).

Ser capaz de usar 'hi' > $sf:Desktop/foo.txt é o melhor dos dois mundos: robusto _e_ conveniente.

Ser capaz de usar 'hi'> $ sf: Desktop / foo.txt é o melhor dos dois mundos: robusto e conveniente.

Totalmente de acordo.

Eu também não vejo a sintaxe como confusa; muitas variáveis ​​de ambiente comumente usadas contêm caminhos e fazemos coisas como mkdir $Env:TEMP\xyz o tempo todo. Acho que não é tão difícil aprender que outro prefixo dá acesso a um conjunto adicional de caminhos - que um novo provedor está em andamento, há um detalhe de implementação, não importante no caso de uso típico de apenas querer saber o caminho.

E, possivelmente, _writing_ em $sf:Something poderia ter o equivalente ao meu Move-KnownFolder ...
... ou talvez não, porque seria muito fácil fazer isso por acidente e a operação pode ser perigosa para os dados do usuário.

Sim, acho que seria muito perigoso, portanto, um cmdlet Move-KnownFolder é preferível, embora - ao contrário de Get-KnownFolder - seja específico do Windows.

A propósito, a enum Environment + SpecialFolder , baseada na função SHGetFolderPath anterior ao Vista, não

Na verdade, a API de pastas conhecidas permite que os aplicativos registrem novas pastas no sistema, de forma que a lista não seja fixa.

Eu executei isso em meu sistema para ver quais Pastas conhecidas com caminhos não vazios foram cobertas pelo método SpecialFolder enum / GetFolderPath e das 95 pastas conhecidas 46 não foram expostas por GetFolderPath:

$specialFolderPaths = [enum]::GetValues([Environment+SpecialFolder]) | % { $p = [Environment]::GetFolderPath($_); [pscustomobject]@{SFName=$_; Path=$p}}
$allKnownFolders = Get-KnownFolder -All
$knownFoldersWithNonEmptyPath = $allKnownFolders | ? Path -ne $null | select Category,Name,CanRedirect,Path
$knownFoldersJoinedWithSpecialFolderPaths = $knownFoldersWithNonEmptyPath | % { $kf = $_; $sfp = $specialFolderPaths | ? Path -eq $kf.Path; $kf | select *,@{Name='SFP';Expression={$sfp}}}
$allKnownFolders | measure | select -expand Count
139
$knownFoldersWithNonEmptyPath | measure | select -expand Count
95
$knownFoldersJoinedWithSpecialFolderPaths | ? SFP -eq $null | measure | select -expand Count
46

No Windows, gostaria de poder acessar todas as pastas conhecidas presentes no sistema (a implementação poderia enumerá-las durante a inicialização). Em outras plataformas, um determinado conjunto básico provavelmente deve ser definido, conforme descrito anteriormente neste tópico (mas não necessariamente limitado aos valores [Environment + SpecialFolder]).

Uma coisa a ser observada - alguns nomes de pastas conhecidas contêm espaços ("Rolo da câmera"); isso não deve ser um problema com a sintaxe proposta, porque já precisamos lidar com caracteres problemáticos em nomes de variáveis ​​de ambiente ( ${Env:ProgramFiles(x86)} ).

Esta página foi útil?
0 / 5 - 0 avaliações