¿Cómo cambio el nombre de todas las carpetas y archivos a minúsculas en Linux?


179

Tengo que cambiar el nombre de un árbol de carpetas completo de forma recursiva para que no aparezca ninguna letra mayúscula en ninguna parte (es el código fuente de C ++, pero eso no debería importar).

Puntos de bonificación por ignorar los archivos / carpetas de control de versiones de CVS y Subversion. La forma preferida sería un script de shell, ya que un shell debería estar disponible en cualquier caja de Linux.

Hubo algunos argumentos válidos sobre los detalles del cambio de nombre del archivo.

  1. Creo que los archivos con los mismos nombres en minúsculas deberían sobrescribirse; Es el problema del usuario. Cuando se desprotege en un sistema de archivos que ignora las mayúsculas y minúsculas, también sobrescribirá el primero con el último.

  2. Consideraría los caracteres AZ y los transformaría en az, todo lo demás solo requiere problemas (al menos con el código fuente).

  3. El script sería necesario para ejecutar una compilación en un sistema Linux, por lo que creo que los cambios en los archivos de control de versión CVS o Subversion deberían omitirse. Después de todo, es solo un pago desde cero. Quizás una "exportación" sea más apropiada.


Lo publicado anteriormente funcionará perfectamente fuera de la caja o con algunos ajustes para casos simples, pero hay algunas situaciones que es posible que desee tener en cuenta antes de ejecutar el cambio de nombre del lote: 1. ¿Qué debería suceder si tiene dos o más nombres en el mismo nivel en la jerarquía de ruta que difiere solo por caso, como ABCdef, abcDEFy aBcDeF? ¿Debería el script renombrar abortar o simplemente advertir y continuar? 2. ¿Cómo define minúsculas para nombres que no sean ASCII de EE. UU.? Si tales nombres pueden estar presentes, ¿se debe verificar primero y excluir el pase? 3. Si está ejecutando una operación de cambio de nombre
Mihai Limbășan

Respuestas:


179

Una versión concisa usando el "rename"comando:

find my_root_dir -depth -exec rename 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \;

Esto evita problemas con el cambio de nombre de los directorios antes que los archivos y el intento de mover archivos a directorios no existentes (por ejemplo, "A/A"a"a/a" ).

O una versión más detallada sin usar "rename" .

for SRC in `find my_root_dir -depth`
do
    DST=`dirname "${SRC}"`/`basename "${SRC}" | tr '[A-Z]' '[a-z]'`
    if [ "${SRC}" != "${DST}" ]
    then
        [ ! -e "${DST}" ] && mv -T "${SRC}" "${DST}" || echo "${SRC} was not renamed"
    fi
done

PD

Este último permite más flexibilidad con el comando mover (por ejemplo, "svn mv").


36
el uso de rename se puede hacer de esta manera, así como renombrar 'y / AZ / az /' *
Tzury Bar Yochay

3
Cuidado, ambas versiones no funcionarán en un sistema de archivos sin distinción entre mayúsculas y minúsculas. En mi caso, reemplacé la thenlínea cerrada por (mv "${SRC}" "${DST}.renametmp" && mv "${DST}.renametmp" "${DST}") || echo "${SRC} was not renamed".
Lloeki

66
El segundo enfoque no funcionó correctamente para mí con archivos que contienen espacios en blanco (contexto: linux, bash).
tenue

44
Usando la última versión de rename, no se necesita regex. El comando completo se convierte find my_root_dir -depth -exec rename -c {} \;. Agregue -f para cambiar el nombre si está en un sistema de archivos que no distingue entre mayúsculas y minúsculas (por ejemplo, Mac)
Javache

El segundo funcionó para mí en Solaris eliminando -Tel mvcomando.
Jamón

261

Más pequeño todavía me gusta bastante:

rename 'y/A-Z/a-z/' *

En sistemas de archivos que no distinguen entre mayúsculas y minúsculas, como HFS + de OS X , querrá agregar el -findicador:

rename -f 'y/A-Z/a-z/' *

3
linux.icydog.net/rename.php :The renaming utility that comes by default with Ubuntu is a Perl program sometimes called prename
sleepsort

1
Gracias, lo usé de esta manera $ find | xargs cambia el nombre 'y / AZ / az /' *
Rashi el

55
Esto supone que tiene perls rename, que no es el caso siempre. por ejemplo, en mi cambio de nombre debian es completamente diferente
Krzysztof Krason

10
En Arco, el programa se llama perl-rename(y es proporcionado por un paquete con el mismo nombre)
jaymmer - Restablecer Monica

3
Esto no responde al problema dado ya que "renombrar" no es recursivo.
Cybolic

89
for f in `find`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done

Haga un "mkdir -p A / B / C" antes de ejecutar su script.
tzot

2
"find" debe ser reemplazado por cualquier comando que use para obtener una lista de los archivos que desea renombrar; por ejemplo, "ls *. {C, CPP, H}".
JPaget

2
No funciona en OS X, solo imprime la información de uso find. find .Parece arreglar esto.
emlai

3
La versión "consolidada", de todos los comentarios (la respuesta ya no se puede editar):for f in `find .`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done
romaricdrigon

1
¿Alguien puede explicarme por qué esto debería funcionar en primer lugar? Si encuentra ./FOO y ./FOO/BAR, primero cambia el nombre de ./FOO a ./foo, después de lo cual ya no puede encontrar ./FOO/BAR. Ingresando mi propia respuesta a continuación que debería solucionar esto.
oisyn

80

Simplemente intente lo siguiente si no necesita preocuparse por la eficiencia.

zip -r foo.zip foo/*
unzip -LL foo.zip

13
Wow, esa es una manera bastante ineficiente.
Artjom B.

28
Asumir que la eficiencia computacional generalmente NO es una preocupación cuando necesita cambiar el nombre de los archivos en un directorio ... Creo que esta es probablemente la solución más creativa en este hilo. Realmente algo tan trivial no debería ser tan complicado como lo demuestran las respuestas aceptadas y esta solución es mucho más proporcional a la cantidad de pensamiento que quiero invertir en una operación como esta. +1000
Peter M. Elias

27
Puede acelerarlo con -0(Eso es 'cero', sin compresión) del interruptor, zip -0 -r foo.zip foo/.
Johnny Baloney

3
Tuve que cambiar más de 50'000 nombres de archivo en diferentes estructuras de directorios. ¡La respuesta original aceptada apenas hizo el 10% del trabajo en 2 minutos donde esto, con la -0compresión, fue casi instantáneo!
Peon

2
Esto no es solo ineficiente. Esto es simplemente imposible si no hay suficiente espacio libre para un archivo temporal.
Victor Yarema el

19

Hombre, a ustedes / chicas les gusta complicar más las cosas ... Uso:

rename 'y/A-Z/a-z/' *

2
Esto no funciona, dice que el archivo con nombre en minúsculas ya existe.
kirhgoff

@kirhgoff He visto que eso sucede con las monturas NFS: siga las instrucciones aquí .
joebeeson 01 de

¿Cómo podríamos cambiar el nombre de un sistema de archivos sensible a mayúsculas y minúsculas? rename 'y/a-z/A-Z/' /tmp/*.jpg-> Can't rename /tmp/*.jpg /TMP/*.JPG: No such file or directory, es decir, intente cambiar el nombre de la ruta completa en lugar de solo el archivo.
Pablo A

14

La mayoría de las respuestas anteriores son peligrosas, ya que no tratan con nombres que contienen caracteres extraños. Su apuesta más segura para este tipo de cosas es usar findla -print0opción, que terminará los nombres de archivo con ASCII NUL en lugar de \ n.

Aquí hay un script, que solo altera los archivos y no los nombres de directorio para no confundir find:

find .  -type f -print0 | xargs -0n 1 bash -c \
's=$(dirname "$0")/$(basename "$0");
d=$(dirname "$0")/$(basename "$0"|tr "[A-Z]" "[a-z]"); mv -f "$s" "$d"'

Lo probé y funciona con nombres de archivos que contienen espacios, todo tipo de citas, etc. Esto es importante porque si ejecuta, como root, uno de esos otros scripts en un árbol que incluye el archivo creado por

touch \;\ echo\ hacker::0:0:hacker:\$\'\057\'root:\$\'\057\'bin\$\'\057\'bash

... bien adivina que ...


Sería más fácil de usar en renamelugar de mv.
Victor Yarema

14

Esto funciona si ya tiene o configuró el comando de cambio de nombre (por ejemplo, a través de brew install en Mac):

rename --lower-case --force somedir/*

9
... si configura el cambio de nombre primero, por ejemplo, a través debrew install rename
pduersteler el

3
renombrar 'y / AZ / az /' *
Stephen

44
--lower-case? Ese no es el caso en Arch Linux al menos, lamentablemente.

3
renameNo es un bash incorporado. Es una utilidad externa y la sintaxis diferirá según la versión que haya instalado. Esta respuesta no es suficiente como una solución portátil "para todos los sistemas operativos basados ​​en Unix" como se afirma.
Corey Goldberg el

2
Recién marcado, el renamecomando incluido en el último Fedora (28), a través del util-linux-2.31paquete, no tiene la --lower-caseopción.
niXar

11

Esto funciona en CentOS / Red Hat Linux u otras distribuciones sin el renamescript Perl:

for i in $( ls | grep [A-Z] ); do mv -i "$i" "`echo $i | tr 'A-Z' 'a-z'`"; done

Fuente: cambie el nombre de todos los nombres de archivo de mayúsculas a minúsculas

(En algunas distribuciones, el renamecomando predeterminado proviene de util-linux, y esa es una herramienta diferente e incompatible).


Esto solo funciona en un solo directorio, no de forma recursiva
Bram

Coool .. Funciona bien
Manikandan Ram

6

El enfoque más simple que encontré en Mac OS X fue usar el paquete de cambio de nombre de http://plasmasturm.org/code/rename/ :

brew install rename
rename --force --lower-case --nows *

--force Rename incluso cuando ya existe un archivo con el nombre de destino.

--lower-case Convierte nombres de archivos a minúsculas.

--nows Reemplaza todas las secuencias de espacios en blanco en el nombre del archivo con caracteres de subrayado único.


5

Aquí está mi solución subóptima, usando un script de shell Bash:

#!/bin/bash
# First, rename all folders
for f in `find . -depth ! -name CVS -type d`; do
   g=`dirname "$f"`/`basename "$f" | tr '[A-Z]' '[a-z]'`
   if [ "xxx$f" != "xxx$g" ]; then
      echo "Renaming folder $f"
      mv -f "$f" "$g"
   fi
done

# Now, rename all files
for f in `find . ! -type d`; do
   g=`dirname "$f"`/`basename "$f" | tr '[A-Z]' '[a-z]'`
   if [ "xxx$f" != "xxx$g" ]; then
      echo "Renaming file $f"
      mv -f "$f" "$g"
   fi
done

Todas las carpetas se renombraron correctamente y mv no hacen preguntas cuando los permisos no coinciden, y las carpetas CVS no se renombran (los archivos de control CVS dentro de esa carpeta todavía se renombran, desafortunadamente).

Como "find -depth" y "find | sort -r" devuelven la lista de carpetas en un orden utilizable para cambiar el nombre, preferí usar "-depth" para buscar carpetas.


Tu primer hallazgo no funciona. Pruebe "mkdir -p A / B / C" y luego ejecute su script.
tzot

4

La pregunta original pedía ignorar los directorios SVN y CVS, lo que se puede hacer agregando -prune al comando find. Por ejemplo, para ignorar CVS:

find . -name CVS -prune -o -exec mv '{}' `echo {} | tr '[A-Z]' '[a-z]'` \; -print

[editar] Probé esto, e incrustar la traducción en minúsculas dentro del hallazgo no funcionó por razones que realmente no entiendo. Entonces, modifique esto para:

$> cat > tolower
#!/bin/bash
mv $1 `echo $1 | tr '[:upper:]' '[:lower:]'`
^D
$> chmod u+x tolower 
$> find . -name CVS -prune -o -exec tolower '{}'  \;

Ian


4

Usando el fijador de nombre de archivo de Larry Wall:

$op = shift or die $help;
chomp(@ARGV = <STDIN>) unless @ARGV;
for (@ARGV) {
    $was = $_;
    eval $op;
    die $@ if $@;
    rename($was,$_) unless $was eq $_;
}

Es tan simple como

find | fix 'tr/A-Z/a-z/'

(¿Dónde fixestá, por supuesto, el guión anterior)


3

Este es un pequeño script de shell que hace lo que solicitó:

root_directory="${1?-please specify parent directory}"
do_it () {
    awk '{ lc= tolower($0); if (lc != $0) print "mv \""  $0 "\" \"" lc "\"" }' | sh
}
# first the folders
find "$root_directory" -depth -type d | do_it
find "$root_directory" ! -type d | do_it

Tenga en cuenta la acción de profundidad en el primer hallazgo.


1
Lo único que funcionó en una Mac de todo el hilo. ¡Montones de gracias!
YemSalat

2

No portátil, solo Zsh, pero bastante conciso.

Primero, asegúrese de que zmvesté cargado.

autoload -U zmv

Además, asegúrese de que extendedglobesté encendido:

setopt extendedglob

Luego use:

zmv '(**/)(*)~CVS~**/CVS' '${1}${(L)2}'

Para recursivamente archivos y directorios en minúsculas donde el nombre no es CVS .


2
for f in `find -depth`; do mv ${f} ${f,,} ; done

find -depthimprime cada archivo y directorio, con el contenido de un directorio impreso antes del directorio en sí. ${f,,}en minúsculas el nombre del archivo.


Esto no funciona: cambia el nombre de un directorio antes de que funcione en los contenidos, de modo que el intento posterior de los contenidos falla.
Pode

Agregar -deptharreglos que! Esta es una solución realmente rápida, pero, por supuesto, sin usar la -print0opción de buscar, no es la más confiable
Jpaugh

@jpaugh No, no lo hace. Intentará cambiar el nombre de ./FOO/BAR a ./foo/bar, pero ./foo aún no existe en ese momento.
oisyn

2

Con MacOS

Instala el renamepaquete,

brew install rename

Utilizar,

find . -iname "*.py" -type f | xargs -I% rename -c -f  "%"                       

Este comando busca todos los archivos con una *.pyextensión y convierte los nombres de los archivos a minúsculas.

`f` - forces a rename

Por ejemplo,

$ find . -iname "*.py" -type f
./sample/Sample_File.py
./sample_file.py
$ find . -iname "*.py" -type f | xargs -I% rename -c -f  "%"
$ find . -iname "*.py" -type f
./sample/sample_file.py
./sample_file.py

¿Por qué usaste la -Iopción por xargsaquí?
Victor Yarema

La - Ibandera no es obligatoria. Se usa cuando ejecutamos múltiples comandos con xargs. Aquí hay un ejemplo del uso de múltiples comandos con xargs cat foo.txt | xargs -I % sh -c 'echo %; mkdir %' Puede ejecutar sin -Icomofind . -iname "*.py" -type f | xargs -I% rename -c -f "%"
akilesh raj

2

Usar tipografía :

typeset -l new        # Always lowercase
find $topPoint |      # Not using xargs to make this more readable
  while read old
  do new="$old"       # $new is a lowercase version of $old
     mv "$old" "$new" # Quotes for those annoying embedded spaces
  done

En Windows, las emulaciones, como Git Bash , pueden fallar porque Windows no distingue entre mayúsculas y minúsculas. Para ellos, agregue un paso que mves el archivo a otro nombre primero, como "$ old.tmp", y luego a $new.


1

Largo pero "funciona sin sorpresas ni instalaciones"

Este script maneja nombres de archivos con espacios, comillas, otros caracteres inusuales y Unicode, funciona en sistemas de archivos que no distinguen entre mayúsculas y minúsculas y en la mayoría de los entornos Unix-y que tienen instalado bash y awk (es decir, casi todos). También informa sobre colisiones si las hay (dejando el nombre de archivo en mayúsculas) y, por supuesto, cambia el nombre de los archivos y directorios y funciona de forma recursiva. Finalmente, es altamente adaptable: puede ajustar el comando find para apuntar a los archivos / directorios que desee y puede ajustar awk para hacer otras manipulaciones de nombres. Tenga en cuenta que con "maneja Unicode" quiero decir que efectivamente convertirá su caso (no los ignorará como las respuestas que usan tr).

# adapt the following command _IF_ you want to deal with specific files/dirs
find . -depth -mindepth 1 -exec bash -c '
  for file do
    # adapt the awk command if you wish to rename to something other than lowercase
    newname=$(dirname "$file")/$(basename "$file" | awk "{print tolower(\$0)}")
    if [ "$file" != "$newname" ] ; then
        # the extra step with the temp filename is for case-insensitive filesystems
        if [ ! -e "$newname" ] && [ ! -e "$newname.lcrnm.tmp" ] ; then
           mv -T "$file" "$newname.lcrnm.tmp" && mv -T "$newname.lcrnm.tmp" "$newname" 
        else
           echo "ERROR: Name already exists: $newname"
        fi
    fi    
  done
' sh {} +

Referencias

Mi guión se basa en estas excelentes respuestas:

/unix/9496/looping-through-files-with-spaces-in-the-names

¿Cómo convertir una cadena a minúsculas en Bash?


1

Esto también funciona bien en macOS:

ruby -e "Dir['*'].each { |p| File.rename(p, p.downcase) }"

Esto es especialmente bueno ya que Ruby viene con macOS.
jxmorris12

1

Necesitaba hacer esto en una configuración de Cygwin en Windows 7 y descubrí que recibí errores de sintaxis con las sugerencias anteriores que probé (aunque es posible que haya perdido una opción de trabajo). Sin embargo, esta solución directamente de los foros de Ubuntu funcionó de la lata :-)

ls | while read upName; do loName=`echo "${upName}" | tr '[:upper:]' '[:lower:]'`; mv "$upName" "$loName"; done

(Nota: anteriormente había reemplazado espacios en blanco con guiones bajos usando:

for f in *\ *; do mv "$f" "${f// /_}"; done

)


1

En OS X, mv -fmuestra el error "mismo archivo", así que cambio el nombre dos veces:

for i in `find . -name "*" -type f |grep -e "[A-Z]"`; do j=`echo $i | tr '[A-Z]' '[a-z]' | sed s/\-1$//`; mv $i $i-1; mv $i-1 $j; done

1

Llegaría a Python en esta situación, para evitar asumir caminos optimistas sin espacios ni barras. También he descubierto que python2tiende a instalarse en más lugares que rename.

#!/usr/bin/env python2
import sys, os

def rename_dir(directory):
  print('DEBUG: rename('+directory+')')

  # Rename current directory if needed
  os.rename(directory, directory.lower())
  directory = directory.lower()

  # Rename children
  for fn in os.listdir(directory):
    path = os.path.join(directory, fn)
    os.rename(path, path.lower())
    path = path.lower()

    # Rename children within, if this child is a directory
    if os.path.isdir(path):
        rename_dir(path)

# Run program, using the first argument passed to this Python script as the name of the folder
rename_dir(sys.argv[1])

Hable acerca de hacer que sea complicado ... simplemente haga ... renombrar 'y / AZ / az /' *
Stephen

1
@Stephen, eso supone que tiene acceso de administrador para instalar renombrar. Con frecuencia necesito combinar datos en sistemas en los que no tengo la capacidad de instalar otro software. En Ubuntus reciente, esto solo se instala si Perl está presente.
John Foley

1

Si usa Arch Linux , puede instalar rename) el paquete AURque proporciona el renamexmcomando como /usr/bin/renamexmejecutable y una página de manual junto con él.

Es una herramienta realmente poderosa para renombrar rápidamente archivos y directorios.

Convertir a minúsculas

rename -l Developers.mp3 # or --lowcase

Convertir a caja SUPERIOR

rename -u developers.mp3 # or --upcase, long option

Otras opciones

-R --recursive # directory and its children

-t --test # Dry run, output but don't rename

-o --owner # Change file owner as well to user specified

-v --verbose # Output what file is renamed and its new name

-s/str/str2 # Substitute string on pattern

--yes # Confirm all actions

Puede obtener el archivo de ejemplo Developers.mp3 desde aquí , si es necesario;)


1
cambiar brewel nombre de en macOS ha -lobligado a crear un enlace simbólico y -ca la conversión a minúsculas, así que ten cuidado.
rien333

0
( find YOURDIR -type d | sort -r;
  find yourdir -type f ) |
grep -v /CVS | grep -v /SVN |
while read f; do mv -v $f `echo $f | tr '[A-Z]' '[a-z]'`; done

Primero cambie el nombre de los directorios de abajo hacia arriba ordenar -r (donde -depth no está disponible), luego los archivos. Luego grep -v / CVS en lugar de find ...- pode porque es más simple. Para directorios grandes, para f en ... puede desbordar algunos búferes de shell. Utilizar buscar ... | mientras lee para evitar eso.

Y sí, esto bloqueará los archivos que difieren solo en caso de que ...


Primero: find YOURDIR -type d | sort -res demasiado problema. Que desea find YOURDIR -depth -type d. En segundo lugar, find -type fDEBE ejecutarse después de que los directorios hayan cambiado de nombre.
tzot

0
find . -depth -name '*[A-Z]*'|sed -n 's/\(.*\/\)\(.*\)/mv -n -v -T \1\2 \1\L\2/p'|sh

No he probado los scripts más elaborados que se mencionan aquí, pero ninguna de las versiones de línea de comandos me funcionó en mi Synology NAS. renameno está disponible, y muchas de las variaciones de findfalla porque parece quedarse con el nombre anterior de la ruta ya renombrada (por ejemplo, si encuentra ./FOOseguido ./FOO/BAR, renombrar ./FOOa ./foocontinuará en la lista ./FOO/BARaunque esa ruta ya no sea válida) . El comando anterior funcionó para mí sin ningún problema.

Lo que sigue es una explicación de cada parte del comando:


find . -depth -name '*[A-Z]*'

Esto buscará cualquier archivo del directorio actual (cambie .a cualquier directorio que desee procesar), utilizando una búsqueda profunda (por ejemplo, aparecerá en la lista ./foo/barantes ./foo), pero solo para los archivos que contienen un carácter en mayúscula. El -namefiltro solo se aplica al nombre del archivo base, no a la ruta completa. Entonces esto aparecerá en la lista ./FOO/BARpero no ./FOO/bar. Esto está bien, ya que no queremos cambiar el nombre ./FOO/bar. Sin ./FOOembargo, queremos cambiar el nombre , pero ese aparece más adelante (por eso -depthes importante).

Este comando en sí mismo es particularmente útil para encontrar los archivos cuyo nombre desea cambiar en primer lugar. Use esto después del comando de cambio de nombre completo para buscar archivos que aún no se hayan reemplazado debido a colisiones o errores de nombre de archivo.


sed -n 's/\(.*\/\)\(.*\)/mv -n -v -T \1\2 \1\L\2/p'

Esta parte lee los archivos generados por findy los formatea en un mvcomando utilizando una expresión regular. La -nopción deja sedde imprimir la entrada, y el pcomando en la expresión regular de búsqueda y reemplazo genera el texto reemplazado.

La expresión regular en sí consta de dos capturas: la parte hasta el último / (que es el directorio del archivo) y el nombre del archivo en sí. El directorio se deja intacto, pero el nombre del archivo se transforma en minúsculas. Entonces, si findsale ./FOO/BAR, se convertirá mv -n -v -T ./FOO/BAR ./FOO/bar. La -nopción de mvasegura que los archivos minúsculos existentes no se sobrescriban. La -vopción realiza la mvsalida de cada cambio que realiza (o no realiza, si ./FOO/barya existe, genera algo como ./FOO/BAR -> ./FOO/BAR, señalando que no se ha realizado ningún cambio). El -Tes muy importante en este caso - que trata el archivo de destino como un directorio. Esto asegurará que ./FOO/BARno se traslade ./FOO/barsi ese directorio existe.

Use esto junto con findpara generar una lista de comandos que se ejecutarán (útil para verificar lo que se hará sin realmente hacerlo)


sh

Esto se explica por sí mismo. Enruta todos los mvcomandos generados al intérprete de shell. Puede reemplazarlo con basho cualquier caparazón de su agrado.


0

Slugify Rename (regex)

No es exactamente lo que solicitó el OP, sino lo que esperaba encontrar en esta página:

Una versión "slugify" para renombrar archivos para que sean similares a las URL (es decir, solo incluyen caracteres alfanuméricos, puntos y guiones):

rename "s/[^a-zA-Z0-9\.]+/-/g" filename

2
"no exactamente"? esto no es para nada lo que pidió el OP
Corey Goldberg
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.